Sat Apr 26 2014 22:01:49

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

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 QUEUE_EVENT_VARIABLES   3
#define queue_t_ref(q, tag)   queue_ref(q)
#define queue_t_unref(q, tag)   queue_unref(q)
#define queues_t_link(c, q, tag)   ao2_t_link(c, q, tag)
#define queues_t_unlink(c, q, tag)   ao2_t_unlink(c, q, tag)
#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  {
  OPT_MARK_AS_ANSWERED = (1 << 0), OPT_GO_ON = (1 << 1), OPT_DATA_QUALITY = (1 << 2), OPT_CALLEE_GO_ON = (1 << 3),
  OPT_CALLEE_HANGUP = (1 << 4), OPT_CALLER_HANGUP = (1 << 5), OPT_IGNORE_CALL_FW = (1 << 6), OPT_IGNORE_CONNECTEDLINE = (1 << 7),
  OPT_CALLEE_PARK = (1 << 8), OPT_CALLER_PARK = (1 << 9), OPT_NO_RETRY = (1 << 10), OPT_RINGING = (1 << 11),
  OPT_RING_WHEN_RINGING = (1 << 12), OPT_CALLEE_TRANSFER = (1 << 13), OPT_CALLER_TRANSFER = (1 << 14), OPT_CALLEE_AUTOMIXMON = (1 << 15),
  OPT_CALLER_AUTOMIXMON = (1 << 16), OPT_CALLEE_AUTOMON = (1 << 17), OPT_CALLER_AUTOMON = (1 << 18)
}
enum  { OPT_ARG_CALLEE_GO_ON = 0, OPT_ARG_ARRAY_SIZE }
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  member_properties { MEMBER_PENALTY = 0, MEMBER_RINGINUSE = 1 }
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 (call_queue, DATA_EXPORT_CALL_QUEUE)
 AST_DATA_STRUCTURE (member, DATA_EXPORT_MEMBER)
 AST_DATA_STRUCTURE (queue_ent, DATA_EXPORT_QUEUE_ENT)
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 int can_ring_entry (struct queue_ent *qe, struct callattempt *call)
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, ptrdiff_t word_list_offset)
 Check if a given word is in a space-delimited list.
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_value (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, int ringinuse)
 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, struct ast_state_cb_info *info, 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_load_queue_rt_friendly (const char *queuename)
static struct memberfind_member_by_queuename_and_interface (const char *queuename, const char *interface)
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 char * handle_queue_set_member_ringinuse (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_member_available (struct call_queue *q, struct member *mem)
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 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_member_ringinuse (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 void member_add_to_queue (struct call_queue *queue, struct member *mem)
static void member_call_pending_clear (struct member *mem)
static int member_call_pending_set (struct member *mem)
static int member_cmp_fn (void *obj1, void *obj2, int flags)
static int member_hash_fn (const void *obj, const int flags)
static void member_remove_from_queue (struct call_queue *queue, struct member *mem)
static int member_status_available (int status)
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_delme_members_decrement_followers (void *obj, void *arg, int flag)
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_mem_read (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_mem_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse.
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_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 int queue_member_decrement_followers (void *obj, void *arg, int flag)
static void queue_member_follower_removal (struct call_queue *queue, struct member *mem)
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 remove_members_and_mark_unfound (void *obj, void *arg, int flags)
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 autopause)
 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, struct ast_config *member_config)
 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_help_members (struct call_queue *q, const char *interface, int penalty)
static int set_member_ringinuse_help_members (struct call_queue *q, const char *interface, int ringinuse)
static int set_member_value (const char *queuename, const char *interface, int property, int value)
static int set_member_value_help_members (struct call_queue *q, const char *interface, int property, int value)
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 struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
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, int ringing)
 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.
static int word_in_list (const char *list, const char *word)
 Check if a given word is in a space-delimited list.

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 = AST_BUILDOPT_SUM, .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 log_membername_as_agent = 0
 queues.conf [general] option
static int montype_default = 0
 queues.conf [general] option
static int negative_penalty_invalid = 0
 queues.conf [general] option
static const char *const pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage [] = "Usage: queue pause member <channel> in <queue> reason <reason>\n"
static const char qsmp_cmd_usage [] = "Usage: queue set member penalty <channel> from <queue> <penalty>\n"
static struct ast_data_entry queue_data_providers []
static struct ast_app_option queue_exec_options [128] = { [ 'C' ] = { .flag = OPT_MARK_AS_ANSWERED }, [ 'c' ] = { .flag = OPT_GO_ON }, [ 'd' ] = { .flag = OPT_DATA_QUALITY }, [ 'F' ] = { .flag = OPT_CALLEE_GO_ON , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'i' ] = { .flag = OPT_IGNORE_CALL_FW }, [ 'I' ] = { .flag = OPT_IGNORE_CONNECTEDLINE }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'n' ] = { .flag = OPT_NO_RETRY }, [ 'r' ] = { .flag = OPT_RINGING }, [ 'R' ] = { .flag = OPT_RING_WHEN_RINGING }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'x' ] = { .flag = OPT_CALLEE_AUTOMIXMON }, [ 'X' ] = { .flag = OPT_CALLER_AUTOMIXMON }, [ 'w' ] = { .flag = OPT_CALLEE_AUTOMON }, [ 'W' ] = { .flag = OPT_CALLER_AUTOMON }, }
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 [] = "Usage: queue unpause member <channel> in <queue> reason <reason>\n"
static char * realtime_ringinuse_field
 name of the ringinuse field in the realtime database
static struct rule_lists rule_lists
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>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 1182 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

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

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

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

Definition at line 1197 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 1196 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 1195 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 4086 of file app_queue.c.

Referenced by wait_for_answer().

#define DATA_EXPORT_CALL_QUEUE (   MEMBER)

Definition at line 9604 of file app_queue.c.

#define DATA_EXPORT_MEMBER (   MEMBER)

Definition at line 9669 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT (   MEMBER)

Definition at line 9683 of file app_queue.c.

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

Definition at line 993 of file app_queue.c.

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 989 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 995 of file app_queue.c.

Referenced by load_module().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 1184 of file app_queue.c.

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

#define queue_t_ref (   q,
  tag 
)    queue_ref(q)

Definition at line 1470 of file app_queue.c.

Referenced by leave_queue(), and try_calling().

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

Definition at line 1472 of file app_queue.c.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

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

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

Referenced by wait_our_turn().

#define RES_NOT_DYNAMIC   (-4)

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:
OPT_MARK_AS_ANSWERED 
OPT_GO_ON 
OPT_DATA_QUALITY 
OPT_CALLEE_GO_ON 
OPT_CALLEE_HANGUP 
OPT_CALLER_HANGUP 
OPT_IGNORE_CALL_FW 
OPT_IGNORE_CONNECTEDLINE 
OPT_CALLEE_PARK 
OPT_CALLER_PARK 
OPT_NO_RETRY 
OPT_RINGING 
OPT_RING_WHEN_RINGING 
OPT_CALLEE_TRANSFER 
OPT_CALLER_TRANSFER 
OPT_CALLEE_AUTOMIXMON 
OPT_CALLER_AUTOMIXMON 
OPT_CALLEE_AUTOMON 
OPT_CALLER_AUTOMON 

Definition at line 888 of file app_queue.c.

     {
   OPT_MARK_AS_ANSWERED =       (1 << 0),
   OPT_GO_ON =                  (1 << 1),
   OPT_DATA_QUALITY =           (1 << 2),
   OPT_CALLEE_GO_ON =           (1 << 3),
   OPT_CALLEE_HANGUP =          (1 << 4),
   OPT_CALLER_HANGUP =          (1 << 5),
   OPT_IGNORE_CALL_FW =         (1 << 6),
   OPT_IGNORE_CONNECTEDLINE =   (1 << 7),
   OPT_CALLEE_PARK =            (1 << 8),
   OPT_CALLER_PARK =            (1 << 9),
   OPT_NO_RETRY =               (1 << 10),
   OPT_RINGING =                (1 << 11),
   OPT_RING_WHEN_RINGING =      (1 << 12),
   OPT_CALLEE_TRANSFER =        (1 << 13),
   OPT_CALLER_TRANSFER =        (1 << 14),
   OPT_CALLEE_AUTOMIXMON =      (1 << 15),
   OPT_CALLER_AUTOMIXMON =      (1 << 16),
   OPT_CALLEE_AUTOMON =         (1 << 17),
   OPT_CALLER_AUTOMON =         (1 << 18),
};
anonymous enum
Enumerator:
OPT_ARG_CALLEE_GO_ON 
OPT_ARG_ARRAY_SIZE 

Definition at line 910 of file app_queue.c.

     {
   OPT_ARG_CALLEE_GO_ON = 0,
   /* note: this entry _MUST_ be the last one in the enum */
   OPT_ARG_ARRAY_SIZE
};
anonymous enum
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 938 of file app_queue.c.

anonymous enum
Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 949 of file app_queue.c.

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4946 of file app_queue.c.

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

                      {
   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),
};
Enumerator:
MEMBER_PENALTY 
MEMBER_RINGINUSE 

Definition at line 1176 of file app_queue.c.

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 955 of file app_queue.c.

                       {
   QUEUE_RELOAD_PARAMETERS = (1 << 0),
   QUEUE_RELOAD_MEMBER = (1 << 1),
   QUEUE_RELOAD_RULES = (1 << 2),
   QUEUE_RESET_STATS = (1 << 3),
};
Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 1048 of file app_queue.c.

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 1073 of file app_queue.c.


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 8246 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_channel_name(), 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, find_load_queue_rt_friendly(), call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, 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, member::ringinuse, SENTINEL, call_queue::servicelevel, queue_ent::start, member::state_interface, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

{
   struct call_queue *q;
   struct ast_str *out = ast_str_alloca(240);
   int found = 0;
   time_t now = time(NULL);
   struct ao2_iterator queue_iter;
   struct ao2_iterator mem_iter;

   if (argc != 2 && argc != 3) {
      return CLI_SHOWUSAGE;
   }

   if (argc == 3) { /* specific queue */
      if ((q = find_load_queue_rt_friendly(argv[2]))) {
         queue_t_unref(q, "Done with temporary pointer");
      }
   } else if (ast_check_realtime("queues")) {
      /* This block is to find any queues which are defined in realtime but
       * which have not yet been added to the in-core container
       */
      struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
      char *queuename;
      if (cfg) {
         for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
            if ((q = find_load_queue_rt_friendly(queuename))) {
               queue_t_unref(q, "Done with temporary pointer");
            }
         }
         ast_config_destroy(cfg);
      }
   }

   ao2_lock(queues);
   queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      float sl;
      struct call_queue *realtime_queue = NULL;

      ao2_lock(q);
      /* This check is to make sure we don't print information for realtime
       * queues which have been deleted from realtime but which have not yet
       * been deleted from the in-core container. Only do this if we're not
       * looking for a specific queue.
       */
      if (argc < 3 && q->realtime) {
         realtime_queue = find_load_queue_rt_friendly(q->name);
         if (!realtime_queue) {
            ao2_unlock(q);
            queue_t_unref(q, "Done with iterator");
            continue;
         }
         queue_t_unref(realtime_queue, "Queue is already in memory");
      }

      if (argc == 3 && strcasecmp(q->name, argv[2])) {
         ao2_unlock(q);
         queue_t_unref(q, "Done with iterator");
         continue;
      }
      found = 1;

      ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
      if (q->maxlen) {
         ast_str_append(&out, 0, "%d", q->maxlen);
      } else {
         ast_str_append(&out, 0, "unlimited");
      }
      sl = 0;
      if (q->callscompleted > 0) {
         sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
      }
      ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
         int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
         q->callscompleted, q->callsabandoned,sl,q->servicelevel);
      do_print(s, fd, ast_str_buffer(out));
      if (!ao2_container_count(q->members)) {
         do_print(s, fd, "   No Members");
      } else {
         struct member *mem;

         do_print(s, fd, "   Members: ");
         mem_iter = ao2_iterator_init(q->members, 0);
         while ((mem = ao2_iterator_next(&mem_iter))) {
            ast_str_set(&out, 0, "      %s", mem->membername);
            if (strcasecmp(mem->membername, mem->interface)) {
               ast_str_append(&out, 0, " (%s", mem->interface);
               if (mem->state_interface) {
                  ast_str_append(&out, 0, " from %s", mem->state_interface);
               }
               ast_str_append(&out, 0, ")");
            }
            if (mem->penalty) {
               ast_str_append(&out, 0, " with penalty %d", mem->penalty);
            }

            ast_str_append(&out, 0, " (ringinuse %s)", mem->ringinuse ? "enabled" : "disabled");

            ast_str_append(&out, 0, "%s%s%s (%s)",
               mem->dynamic ? " (dynamic)" : "",
               mem->realtime ? " (realtime)" : "",
               mem->paused ? " (paused)" : "",
               ast_devstate2str(mem->status));
            if (mem->calls) {
               ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
                  mem->calls, (long) (time(NULL) - mem->lastcall));
            } else {
               ast_str_append(&out, 0, " has taken no calls yet");
            }
            do_print(s, fd, ast_str_buffer(out));
            ao2_ref(mem, -1);
         }
         ao2_iterator_destroy(&mem_iter);
      }
      if (!q->head) {
         do_print(s, fd, "   No Callers");
      } else {
         struct queue_ent *qe;
         int pos = 1;

         do_print(s, fd, "   Callers: ");
         for (qe = q->head; qe; qe = qe->next) {
            ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
               pos++, ast_channel_name(qe->chan), (long) (now - qe->start) / 60,
               (long) (now - qe->start) % 60, qe->prio);
            do_print(s, fd, ast_str_buffer(out));
         }
      }
      do_print(s, fd, ""); /* blank line between entries */
      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
   }
   ao2_iterator_destroy(&queue_iter);
   ao2_unlock(queues);
   if (!found) {
      if (argc == 3) {
         ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
      } else {
         ast_str_set(&out, 0, "No queues.");
      }
      do_print(s, fd, ast_str_buffer(out));
   }
   return CLI_SUCCESS;
}
static void __reg_module ( void  ) [static]

Definition at line 10041 of file app_queue.c.

static void __unreg_module ( void  ) [static]

Definition at line 10041 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_DYNAMICwhen they aren't a RT member
RES_NOSUCHQUEUEqueue does not exist
RES_OKAYadded member from queue
RES_EXISTSqueue exists but no members
RES_OUT_OF_MEMORYqueue 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 6150 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, find_load_queue_rt_friendly(), member::interface, interface_exists(), is_member_available(), member::lastcall, manager_event, member_add_to_queue(), member::membername, call_queue::name, member::paused, member::penalty, queue_t_unref, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, member::ringinuse, call_queue::ringinuse, and member::status.

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

{
   struct call_queue *q;
   struct member *new_member, *old_member;
   int res = RES_NOSUCHQUEUE;

   /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
    * short-circuits if the queue is already in memory. */
   if (!(q = find_load_queue_rt_friendly(queuename))) {
      return res;
   }

   ao2_lock(q);
   if ((old_member = interface_exists(q, interface)) == NULL) {
      if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface, q->ringinuse))) {
         new_member->ringinuse = q->ringinuse;
         new_member->dynamic = 1;
         member_add_to_queue(q, new_member);
         /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when a member is added to the queue.</synopsis>
            <syntax>
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='StateInterface'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Membership'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Penalty'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='CallsTaken'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='LastCall'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Status'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Paused'])" />
            </syntax>
            <see-also>
               <ref type="managerEvent">QueueMemberRemoved</ref>
               <ref type="application">AddQueueMember</ref>
            </see-also>
         </managerEventInstance>
         ***/
         manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
            "Queue: %s\r\n"
            "Location: %s\r\n"
            "MemberName: %s\r\n"
            "StateInterface: %s\r\n"
            "Membership: %s\r\n"
            "Penalty: %d\r\n"
            "CallsTaken: %d\r\n"
            "LastCall: %d\r\n"
            "Status: %d\r\n"
            "Paused: %d\r\n",
            q->name, new_member->interface, new_member->membername, state_interface,
            "dynamic",
            new_member->penalty, new_member->calls, (int) new_member->lastcall,
            new_member->status, new_member->paused);

         if (is_member_available(q, new_member)) {
            ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
         }

         ao2_ref(new_member, -1);
         new_member = NULL;

         if (dump) {
            dump_queue_members(q);
         }

         res = RES_OKAY;
      } else {
         res = RES_OUTOFMEMORY;
      }
   } else {
      ao2_ref(old_member, -1);
      res = RES_EXISTS;
   }
   ao2_unlock(q);
   queue_t_unref(q, "Expiring temporary reference");

   return res;
}
static struct call_queue* alloc_queue ( const char *  queuename) [static, read]

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

{
   struct call_queue *q;

   if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
      if (ast_string_field_init(q, 64)) {
         queue_t_unref(q, "String field allocation failed");
         return NULL;
      }
      ast_string_field_set(q, name, queuename);
   }
   return q;
}
static int aqm_exec ( struct ast_channel chan,
const char *  data 
) [static]

AddQueueMember application.

Definition at line 6798 of file app_queue.c.

References add_to_queue(), args, AST_APP_ARG, ast_channel_name(), ast_channel_uniqueid(), AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), member::interface, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, parse(), pbx_builtin_setvar_helper(), member::penalty, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and member::state_interface.

Referenced by load_module().

{
   int res=-1;
   char *parse, *temppos = NULL;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(interface);
      AST_APP_ARG(penalty);
      AST_APP_ARG(options);
      AST_APP_ARG(membername);
      AST_APP_ARG(state_interface);
   );
   int penalty = 0;

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (ast_strlen_zero(args.interface)) {
      args.interface = ast_strdupa(ast_channel_name(chan));
      temppos = strrchr(args.interface, '-');
      if (temppos) {
         *temppos = '\0';
      }
   }

   if (!ast_strlen_zero(args.penalty)) {
      if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
         ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
         penalty = 0;
      }
   }

   switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
   case RES_OKAY:
      if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "ADDMEMBER", "%s", "");
      } else {
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername, "ADDMEMBER", "%s", "");
      }
      ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
      pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
      res = 0;
      break;
   case RES_EXISTS:
      ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
      pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
      res = 0;
      break;
   case RES_NOSUCHQUEUE:
      ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
      pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
      res = 0;
      break;
   case RES_OUTOFMEMORY:
      ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
      break;
   }

   return res;
}
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 5077 of file app_queue.c.

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

{
   return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
}
static int autopause2int ( const char *  autopause) [static]

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

{
   int x;
   /*This 'double check' that default value is OFF */
   if (ast_strlen_zero(autopause)) {
      return QUEUE_AUTOPAUSE_OFF;
   }

   /*This 'double check' is to ensure old values works */
   if(ast_true(autopause)) {
      return QUEUE_AUTOPAUSE_ON;
   }

   for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
      if (!strcasecmp(autopause, autopausesmodes[x].name)) {
         return autopausesmodes[x].autopause;
      }
   }

   /*This 'double check' that default value is OFF */
   return QUEUE_AUTOPAUSE_OFF;
}
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:
-1if penalties are exceeded
0otherwise

Definition at line 4873 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_ent::pos, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, QUEUE_STRATEGY_WRANDOM, member::queuepos, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

{
   /* disregarding penalty on too few members? */
   int membercount = ao2_container_count(q->members);
   unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;

   if (usepenalty) {
      if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) ||
         (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) {
         return -1;
      }
   } else {
      ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
           membercount, q->penaltymemberslimit);
   }

   switch (q->strategy) {
   case QUEUE_STRATEGY_RINGALL:
      /* Everyone equal, except for penalty */
      tmp->metric = mem->penalty * 1000000 * usepenalty;
      break;
   case QUEUE_STRATEGY_LINEAR:
      if (pos < qe->linpos) {
         tmp->metric = 1000 + pos;
      } else {
         if (pos > qe->linpos) {
            /* Indicate there is another priority */
            qe->linwrapped = 1;
         }
         tmp->metric = pos;
      }
      tmp->metric += mem->penalty * 1000000 * usepenalty;
      break;
   case QUEUE_STRATEGY_RRORDERED:
   case QUEUE_STRATEGY_RRMEMORY:
      pos = mem->queuepos;
      if (pos < q->rrpos) {
         tmp->metric = 1000 + pos;
      } else {
         if (pos > q->rrpos) {
            /* Indicate there is another priority */
            q->wrapped = 1;
         }
         tmp->metric = pos;
      }
      tmp->metric += mem->penalty * 1000000 * usepenalty;
      break;
   case QUEUE_STRATEGY_RANDOM:
      tmp->metric = ast_random() % 1000;
      tmp->metric += mem->penalty * 1000000 * usepenalty;
      break;
   case QUEUE_STRATEGY_WRANDOM:
      tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
      break;
   case QUEUE_STRATEGY_FEWESTCALLS:
      tmp->metric = mem->calls;
      tmp->metric += mem->penalty * 1000000 * usepenalty;
      break;
   case QUEUE_STRATEGY_LEASTRECENT:
      if (!mem->lastcall) {
         tmp->metric = 0;
      } else {
         tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
      }
      tmp->metric += mem->penalty * 1000000 * usepenalty;
      break;
   default:
      ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
      break;
   }
   return 0;
}
static void callattempt_free ( struct callattempt doomed) [static]

Definition at line 3344 of file app_queue.c.

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

Referenced by hangupcalls(), and try_calling().

{
   if (doomed->member) {
      ao2_ref(doomed->member, -1);
   }
   ast_party_connected_line_free(&doomed->connected);
   ast_free(doomed);
}
static int can_ring_entry ( struct queue_ent qe,
struct callattempt call 
) [static]

Definition at line 3554 of file app_queue.c.

References ast_debug, compare_weight(), get_queue_member_status(), callattempt::interface, callattempt::lastcall, callattempt::lastqueue, callattempt::member, member_call_pending_clear(), member_call_pending_set(), member_status_available(), call_queue::name, queue_ent::parent, member::paused, member::ringinuse, member::status, and call_queue::wrapuptime.

Referenced by ring_entry().

{
   if (call->member->paused) {
      ast_debug(1, "%s paused, can't receive call\n", call->interface);
      return 0;
   }

   if (!call->member->ringinuse && !member_status_available(call->member->status)) {
      ast_debug(1, "%s not available, can't receive call\n", call->interface);
      return 0;
   }

   if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
      || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
      ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
         (call->lastqueue ? call->lastqueue->name : qe->parent->name),
         call->interface);
      return 0;
   }

   if (use_weight && compare_weight(qe->parent, call->member)) {
      ast_debug(1, "Priority queue delaying call to %s:%s\n",
         qe->parent->name, call->interface);
      return 0;
   }

   if (!call->member->ringinuse) {
      if (member_call_pending_set(call->member)) {
         ast_debug(1, "%s has another call pending, can't receive call\n",
            call->interface);
         return 0;
      }

      /*
       * The queue member is available.  Get current status to be sure
       * because the device state and extension state callbacks may
       * not have updated the status yet.
       */
      if (!member_status_available(get_queue_member_status(call->member))) {
         ast_debug(1, "%s actually not available, can't receive call\n",
            call->interface);
         member_call_pending_clear(call->member);
         return 0;
      }
   }

   return 1;
}
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:
queuenameThe 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 8184 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().

{
   struct call_queue *q;
   struct ao2_iterator queue_iter;

   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      ao2_lock(q);
      if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
         clear_queue(q);
      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&queue_iter);
   return 0;
}
static int compare_weight ( struct call_queue rq,
struct member member 
) [static]

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

{
   struct call_queue *q;
   struct member *mem;
   int found = 0;
   struct ao2_iterator queue_iter;

   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      if (q == rq) { /* don't check myself, could deadlock */
         queue_t_unref(q, "Done with iterator");
         continue;
      }
      ao2_lock(q);
      if (q->count && q->members) {
         if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
            ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
            if (q->weight > rq->weight && q->count >= num_available_members(q)) {
               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);
               found = 1;
            }
            ao2_ref(mem, -1);
         }
      }
      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
      if (found) {
         break;
      }
   }
   ao2_iterator_destroy(&queue_iter);
   return found;
}
static char* complete_queue ( const char *  line,
const char *  word,
int  pos,
int  state,
ptrdiff_t  word_list_offset 
) [static]

Check if a given word is in a space-delimited list.

Parameters:
lineThe line as typed not including the current word being completed
wordThe word currently being completed
posThe number of completed words in line
stateThe nth desired completion option
word_list_offsetOffset into the line where the list of queues begins. If non-zero, queues in the list will not be offered for further completion.
Returns:
Returns the queue tab-completion for the given word and state

Definition at line 8463 of file app_queue.c.

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

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

{
   struct call_queue *q;
   char *ret = NULL;
   int which = 0;
   int wordlen = strlen(word);
   struct ao2_iterator queue_iter;
   const char *word_list = NULL;

   /* for certain commands, already completed items should be left out of
    * the list */
   if (word_list_offset && strlen(line) >= word_list_offset) {
      word_list = line + word_list_offset;
   }

   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      if (!strncasecmp(word, q->name, wordlen) && ++which > state
         && (!word_list_offset || !word_in_list(word_list, q->name))) {
         ret = ast_strdup(q->name);
         queue_t_unref(q, "Done with iterator");
         break;
      }
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&queue_iter);

   /* Pretend "rules" is at the end of the queues list in certain
    * circumstances since it is an alternate command that should be
    * tab-completable for "queue show" */
   if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
      ret = ast_strdup("rules");
   }

   return ret;
}
static char* complete_queue_add_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 8949 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

{
   /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
   switch (pos) {
   case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
      return NULL;
   case 4: /* only one possible match, "to" */
      return state == 0 ? ast_strdup("to") : NULL;
   case 5: /* <queue> */
      return complete_queue(line, word, pos, state, 0);
   case 6: /* only one possible match, "penalty" */
      return state == 0 ? ast_strdup("penalty") : NULL;
   case 7:
      if (state < 100) {      /* 0-99 */
         char *num;
         if ((num = ast_malloc(3))) {
            sprintf(num, "%d", state);
         }
         return num;
      } else {
         return NULL;
      }
   case 8: /* only one possible match, "as" */
      return state == 0 ? ast_strdup("as") : NULL;
   case 9: /* Don't attempt to complete name of member (infinite possibilities) */
      return NULL;
   default:
      return NULL;
   }
}
static char* complete_queue_pause_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 9227 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

{
   /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
   switch (pos) {
   case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
      return NULL;
   case 4:  /* only one possible match, "queue" */
      return state == 0 ? ast_strdup("queue") : NULL;
   case 5:  /* <queue> */
      return complete_queue(line, word, pos, state, 0);
   case 6: /* "reason" */
      return state == 0 ? ast_strdup("reason") : NULL;
   case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
      return NULL;
   default:
      return NULL;
   }
}
static char* complete_queue_remove_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

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

{
   int which = 0;
   struct call_queue *q;
   struct member *m;
   struct ao2_iterator queue_iter;
   struct ao2_iterator mem_iter;
   int wordlen = strlen(word);

   /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
   if (pos > 5 || pos < 3) {
      return NULL;
   }
   if (pos == 4) {   /* only one possible match, 'from' */
      return (state == 0 ? ast_strdup("from") : NULL);
   }

   if (pos == 5) {   /* No need to duplicate code */
      return complete_queue(line, word, pos, state, 0);
   }

   /* here is the case for 3, <member> */
   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      ao2_lock(q);
      mem_iter = ao2_iterator_init(q->members, 0);
      while ((m = ao2_iterator_next(&mem_iter))) {
         if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
            char *tmp;
            tmp = ast_strdup(m->interface);
            ao2_ref(m, -1);
            ao2_iterator_destroy(&mem_iter);
            ao2_unlock(q);
            queue_t_unref(q, "Done with iterator, returning interface name");
            ao2_iterator_destroy(&queue_iter);
            return tmp;
         }
         ao2_ref(m, -1);
      }
      ao2_iterator_destroy(&mem_iter);
      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&queue_iter);

   return NULL;
}
static char* complete_queue_rule_show ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 9422 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

{
   int which = 0;
   struct rule_list *rl_iter;
   int wordlen = strlen(word);
   char *ret = NULL;
   if (pos != 3) /* Wha? */ {
      return NULL;
   }

   AST_LIST_LOCK(&rule_lists);
   AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
      if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
         ret = ast_strdup(rl_iter->name);
         break;
      }
   }
   AST_LIST_UNLOCK(&rule_lists);

   return ret;
}
static char* complete_queue_set_member_value ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 9301 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty(), and handle_queue_set_member_ringinuse().

{
   /* 0 - queue; 1 - set; 2 - penalty/ringinuse; 3 - <value>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
   switch (pos) {
   case 4:
      if (state == 0) {
         return ast_strdup("on");
      } else {
         return NULL;
      }
   case 6:
      if (state == 0) {
         return ast_strdup("in");
      } else {
         return NULL;
      }
   case 7:
      return complete_queue(line, word, pos, state, 0);
   default:
      return NULL;
   }
}
static char* complete_queue_show ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 8500 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

{
   if (pos == 2) {
      return complete_queue(line, word, pos, state, 0);
   }
   return NULL;
}
static int compress_char ( const char  c) [static]

Definition at line 1960 of file app_queue.c.

Referenced by member_hash_fn().

{
   if (c < 32) {
      return 0;
   } else if (c > 96) {
      return c - 64;
   }
   return c - 32;
}
static void copy_rules ( struct queue_ent qe,
const char *  rulename 
) [static]

Copy rule from global list into specified queue.

Definition at line 6900 of file app_queue.c.

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

Referenced by queue_exec().

{
   struct penalty_rule *pr_iter;
   struct rule_list *rl_iter;
   const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
   AST_LIST_LOCK(&rule_lists);
   AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
      if (!strcasecmp(rl_iter->name, tmp)) {
         break;
      }
   }
   if (rl_iter) {
      AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
         struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
         if (!new_pr) {
            ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
            break;
         }
         new_pr->time = pr_iter->time;
         new_pr->max_value = pr_iter->max_value;
         new_pr->min_value = pr_iter->min_value;
         new_pr->max_relative = pr_iter->max_relative;
         new_pr->min_relative = pr_iter->min_relative;
         AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
      }
   }
   AST_LIST_UNLOCK(&rule_lists);
}
static struct member* create_queue_member ( const char *  interface,
const char *  membername,
int  penalty,
int  paused,
const char *  state_interface,
int  ringinuse 
) [static, read]

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

Definition at line 1924 of file app_queue.c.

References ao2_alloc, ast_copy_string(), ast_log(), ast_strlen_zero(), queue_ent::context, exten, get_queue_member_status(), member::interface, LOG_WARNING, member::membername, member::paused, member::penalty, member::ringinuse, S_OR, member::state_context, member::state_exten, member::state_interface, and member::status.

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

{
   struct member *cur;

   if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
      cur->ringinuse = ringinuse;
      cur->penalty = penalty;
      cur->paused = paused;
      ast_copy_string(cur->interface, interface, sizeof(cur->interface));
      if (!ast_strlen_zero(state_interface)) {
         ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
      } else {
         ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
      }
      if (!ast_strlen_zero(membername)) {
         ast_copy_string(cur->membername, membername, sizeof(cur->membername));
      } else {
         ast_copy_string(cur->membername, interface, sizeof(cur->membername));
      }
      if (!strchr(cur->interface, '/')) {
         ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
      }
      if (!strncmp(cur->state_interface, "hint:", 5)) {
         char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
         char *exten = strsep(&context, "@") + 5;

         ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
         ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
      }
      cur->status = get_queue_member_status(cur);
   }

   return cur;
}
static void destroy_queue ( void *  obj) [static]

Free queue's member list then its string fields.

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

{
   struct call_queue *q = obj;
   int i;

   free_members(q, 1);
   ast_string_field_free_memory(q);
   for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
      if (q->sound_periodicannounce[i]) {
         free(q->sound_periodicannounce[i]);
      }
   }
   ao2_ref(q->members, -1);
}
static void device_state_cb ( const struct ast_event event,
void *  unused 
) [static]

Definition at line 1816 of file app_queue.c.

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

Referenced by load_module().

{
   enum ast_device_state state;
   const char *device;
   struct statechange *sc;
   size_t datapsize;

   state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
   device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);

   if (ast_strlen_zero(device)) {
      ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
      return;
   }
   datapsize = sizeof(*sc) + strlen(device) + 1;
   if (!(sc = ast_calloc(1, datapsize))) {
      ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
      return;
   }
   sc->state = state;
   strcpy(sc->dev, device);
   if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
      ast_free(sc);
   }
}
static void do_hang ( struct callattempt o) [static]

common hangup actions

Definition at line 3450 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

{
   o->stillgoing = 0;
   ast_hangup(o->chan);
   o->chan = NULL;
}
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 8231 of file app_queue.c.

References ast_cli(), and astman_append().

Referenced by __queues_show().

{
   if (s) {
      astman_append(s, "%s\r\n", str);
   } else {
      ast_cli(fd, "%s\n", str);
   }
}
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 6027 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_free, ast_log(), ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_strlen(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::state_interface, and value.

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

{
   struct member *cur_member;
   struct ast_str *value;
   struct ao2_iterator mem_iter;

   if (!pm_queue) {
      return;
   }

   /* 4K is a reasonable default for most applications, but we grow to
    * accommodate more if necessary. */
   if (!(value = ast_str_create(4096))) {
      return;
   }

   mem_iter = ao2_iterator_init(pm_queue->members, 0);
   while ((cur_member = ao2_iterator_next(&mem_iter))) {
      if (!cur_member->dynamic) {
         ao2_ref(cur_member, -1);
         continue;
      }

      ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s",
         ast_str_strlen(value) ? "|" : "",
         cur_member->interface,
         cur_member->penalty,
         cur_member->paused,
         cur_member->membername,
         cur_member->state_interface);

      ao2_ref(cur_member, -1);
   }
   ao2_iterator_destroy(&mem_iter);

   if (ast_str_strlen(value) && !cur_member) {
      if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) {
         ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
      }
   } else {
      /* Delete the entry if the queue is empty or there is an error */
      ast_db_del(pm_family, pm_queue->name);
   }

   ast_free(value);
}
static void end_bridge_callback ( void *  data) [static]

Definition at line 5125 of file app_queue.c.

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

Referenced by try_calling().

{
   struct queue_end_bridge *qeb = data;
   struct call_queue *q = qeb->q;
   struct ast_channel *chan = qeb->chan;

   if (ao2_ref(qeb, -1) == 1) {
      set_queue_variables(q, chan);
      /* This unrefs the reference we made in try_calling when we allocated qeb */
      queue_t_unref(q, "Expire bridge_config reference");
   }
}
static void end_bridge_callback_data_fixup ( struct ast_bridge_config bconfig,
struct ast_channel originator,
struct ast_channel terminator 
) [static]

Definition at line 5118 of file app_queue.c.

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

Referenced by try_calling().

{
   struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
   ao2_ref(qeb, +1);
   qeb->chan = originator;
}
static int extension_state_cb ( char *  context,
char *  exten,
struct ast_state_cb_info info,
void *  data 
) [static]

Definition at line 1874 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(), AST_HINT_UPDATE_DEVICE, ast_state_cb_info::exten_state, extensionstate2devicestate(), call_queue::found, call_queue::members, queue_t_unref, queues, ast_state_cb_info::reason, member::state_context, member::state_exten, and update_status().

Referenced by load_module(), and unload_module().

{
   struct ao2_iterator miter, qiter;
   struct member *m;
   struct call_queue *q;
   int state = info->exten_state;
   int found = 0, device_state = extensionstate2devicestate(state);

   /* only interested in extension state updates involving device states */
   if (info->reason != AST_HINT_UPDATE_DEVICE) {
      return 0;
   }

   qiter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
      ao2_lock(q);

      miter = ao2_iterator_init(q->members, 0);
      for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
         if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
            update_status(q, m, device_state);
            ao2_ref(m, -1);
            found = 1;
            break;
         }
      }
      ao2_iterator_destroy(&miter);

      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&qiter);

        if (found) {
      ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
   } else {
      ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
           exten, context, device_state, ast_devstate2str(device_state));
   }

   return 0;
}
static struct callattempt* find_best ( struct callattempt outgoing) [static, read]

find the entry with the best metric, or NULL

Definition at line 3799 of file app_queue.c.

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

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

{
   struct callattempt *best = NULL, *cur;

   for (cur = outgoing; cur; cur = cur->q_next) {
      if (cur->stillgoing &&              /* Not already done */
         !cur->chan &&              /* Isn't already going */
         (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
         best = cur;
      }
   }

   return best;
}
static struct call_queue* find_load_queue_rt_friendly ( const char *  queuename) [static, read]

note

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 2775 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(), find_member_by_queuename_and_interface(), join_queue(), queue_function_exists(), queue_function_mem_read(), queue_function_mem_write(), queue_function_qac_dep(), queues_data_provider_get(), reload_queue_members(), and set_member_value().

{
   struct ast_variable *queue_vars;
   struct ast_config *member_config = NULL;
   struct call_queue *q = NULL, tmpq = {
      .name = queuename,
   };
   int prev_weight = 0;

   /* Find the queue in the in-core list first. */
   q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");

   if (!q || q->realtime) {
      /*! \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. */

      queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
      if (queue_vars) {
         member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
         if (!member_config) {
            ast_debug(1, "No queue_members defined in config extconfig.conf\n");
            member_config = ast_config_new();
         }
      }
      if (q) {
         prev_weight = q->weight ? 1 : 0;
         queue_t_unref(q, "Need to find realtime queue");
      }

      q = find_queue_by_name_rt(queuename, queue_vars, member_config);
      ast_config_destroy(member_config);
      ast_variables_destroy(queue_vars);

      /* update the use_weight value if the queue's has gained or lost a weight */
      if (q) {
         if (!q->weight && prev_weight) {
            ast_atomic_fetchadd_int(&use_weight, -1);
         }
         if (q->weight && !prev_weight) {
            ast_atomic_fetchadd_int(&use_weight, +1);
         }
      }
      /* Other cases will end up with the proper value for use_weight */
   } else {
      update_realtime_members(q);
   }
   return q;
}
static struct member * find_member_by_queuename_and_interface ( const char *  queuename,
const char *  interface 
) [static, read]

Definition at line 10021 of file app_queue.c.

References ao2_find, ao2_lock, ao2_unlock, find_load_queue_rt_friendly(), call_queue::members, OBJ_KEY, and queue_t_unref.

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

{
   struct member *mem = NULL;
   struct call_queue *q;

   if ((q = find_load_queue_rt_friendly(queuename))) {
      ao2_lock(q);
      mem = ao2_find(q->members, interface, OBJ_KEY);
      ao2_unlock(q);
      queue_t_unref(q, "Expiring temporary reference.");
   }
   return mem;
}
static struct call_queue* find_queue_by_name_rt ( const char *  queuename,
struct ast_variable queue_vars,
struct ast_config member_config 
) [static, read]

Reload a single queue via realtime.

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

Return values:
thequeue,
NULLif 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 2631 of file app_queue.c.

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

Referenced by find_load_queue_rt_friendly().

{
   struct ast_variable *v;
   struct call_queue *q, tmpq = {
      .name = queuename,
   };
   struct member *m;
   struct ao2_iterator mem_iter;
   char *interface = NULL;
   const char *tmp_name;
   char *tmp;
   char tmpbuf[64];  /* Must be longer than the longest queue param name. */

   /* Static queues override realtime. */
   if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
      ao2_lock(q);
      if (!q->realtime) {
         if (q->dead) {
            ao2_unlock(q);
            queue_t_unref(q, "Queue is dead; can't return it");
            return NULL;
         }
         ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
         ao2_unlock(q);
         return q;
      }
   } else if (!member_config) {
      /* Not found in the list, and it's not realtime ... */
      return NULL;
   }
   /* Check if queue is defined in realtime. */
   if (!queue_vars) {
      /* Delete queue from in-core list if it has been deleted in realtime. */
      if (q) {
         /*! \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. */
         ast_debug(1, "Queue %s not found in realtime.\n", queuename);

         q->dead = 1;
         /* Delete if unused (else will be deleted when last caller leaves). */
         queues_t_unlink(queues, q, "Unused; removing from container");
         ao2_unlock(q);
         queue_t_unref(q, "Queue is dead; can't return it");
      }
      return NULL;
   }

   /* Create a new queue if an in-core entry does not exist yet. */
   if (!q) {
      struct ast_variable *tmpvar = NULL;
      if (!(q = alloc_queue(queuename))) {
         return NULL;
      }
      ao2_lock(q);
      clear_queue(q);
      q->realtime = 1;
      /*Before we initialize the queue, we need to set the strategy, so that linear strategy
       * will allocate the members properly
       */
      for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
         if (!strcasecmp(tmpvar->name, "strategy")) {
            q->strategy = strat2int(tmpvar->value);
            if (q->strategy < 0) {
               ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
               tmpvar->value, q->name);
               q->strategy = QUEUE_STRATEGY_RINGALL;
            }
            break;
         }
      }
      /* We traversed all variables and didn't find a strategy */
      if (!tmpvar) {
         q->strategy = QUEUE_STRATEGY_RINGALL;
      }
      queues_t_link(queues, q, "Add queue to container");
   }
   init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */

   memset(tmpbuf, 0, sizeof(tmpbuf));
   for (v = queue_vars; v; v = v->next) {
      /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
      if (strchr(v->name, '_')) {
         ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
         tmp_name = tmpbuf;
         tmp = tmpbuf;
         while ((tmp = strchr(tmp, '_'))) {
            *tmp++ = '-';
         }
      } else {
         tmp_name = v->name;
      }

      /* NULL values don't get returned from realtime; blank values should
       * still get set.  If someone doesn't want a value to be set, they
       * should set the realtime column to NULL, not blank. */
      queue_set_param(q, tmp_name, v->value, -1, 0);
   }

   /* Temporarily set realtime members dead so we can detect deleted ones. */
   mem_iter = ao2_iterator_init(q->members, 0);
   while ((m = ao2_iterator_next(&mem_iter))) {
      if (m->realtime) {
         m->dead = 1;
      }
      ao2_ref(m, -1);
   }
   ao2_iterator_destroy(&mem_iter);

   while ((interface = ast_category_browse(member_config, interface))) {
      rt_handle_member_record(q, interface, member_config);
   }

   /* Delete all realtime members that have been deleted in DB. */
   mem_iter = ao2_iterator_init(q->members, 0);
   while ((m = ao2_iterator_next(&mem_iter))) {
      if (m->dead) {
         if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
            ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
         } else {
            ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
         }
         member_remove_from_queue(q, m);
      }
      ao2_ref(m, -1);
   }
   ao2_iterator_destroy(&mem_iter);

   ao2_unlock(q);

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

Iterate through queue's member list and delete them.

Definition at line 2576 of file app_queue.c.

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

Referenced by destroy_queue().

{
   /* Free non-dynamic members */
   struct member *cur;
   struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);

   while ((cur = ao2_iterator_next(&mem_iter))) {
      if (all || !cur->dynamic) {
         member_remove_from_queue(q, cur);
      }
      ao2_ref(cur, -1);
   }
   ao2_iterator_destroy(&mem_iter);
}
static int get_member_penalty ( char *  queuename,
char *  interface 
) [static]

Definition at line 6525 of file app_queue.c.

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

Referenced by queue_function_memberpenalty_read().

{
   int foundqueue = 0, penalty;
   struct call_queue *q, tmpq = {
      .name = queuename,
   };
   struct member *mem;

   if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
      foundqueue = 1;
      ao2_lock(q);
      if ((mem = interface_exists(q, interface))) {
         penalty = mem->penalty;
         ao2_ref(mem, -1);
         ao2_unlock(q);
         queue_t_unref(q, "Search complete");
         return penalty;
      }
      ao2_unlock(q);
      queue_t_unref(q, "Search complete");
   }

   /* some useful debuging */
   if (foundqueue) {
      ast_log (LOG_ERROR, "Invalid queuename\n");
   } else {
      ast_log (LOG_ERROR, "Invalid interface\n");
   }

   return RESULT_FAILURE;
}
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 1545 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, queue_ent::min_penalty, 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().

{
   struct member *member;
   struct ao2_iterator mem_iter;

   ao2_lock(q);
   mem_iter = ao2_iterator_init(q->members, 0);
   for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
      if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) {
         if (conditions & QUEUE_EMPTY_PENALTY) {
            ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
            continue;
         }
      }

      switch (member->status) {
      case AST_DEVICE_INVALID:
         if (conditions & QUEUE_EMPTY_INVALID) {
            ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
            break;
         }
         goto default_case;
      case AST_DEVICE_UNAVAILABLE:
         if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
            ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
            break;
         }
         goto default_case;
      case AST_DEVICE_INUSE:
         if (conditions & QUEUE_EMPTY_INUSE) {
            ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
            break;
         }
         goto default_case;
      case AST_DEVICE_RINGING:
         if (conditions & QUEUE_EMPTY_RINGING) {
            ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
            break;
         }
         goto default_case;
      case AST_DEVICE_UNKNOWN:
         if (conditions & QUEUE_EMPTY_UNKNOWN) {
            ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
            break;
         }
         /* Fall-through */
      default:
      default_case:
         if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
            ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
            break;
         } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
            ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
            break;
         } else {
            ao2_ref(member, -1);
            ao2_iterator_destroy(&mem_iter);
            ao2_unlock(q);
            ast_debug(4, "%s is available.\n", member->membername);
            return 0;
         }
         break;
      }
   }
   ao2_iterator_destroy(&mem_iter);

   ao2_unlock(q);
   return -1;
}
static int get_queue_member_status ( struct member cur) [static]
static char* handle_queue_add_member ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 9040 of file app_queue.c.

References add_to_queue(), ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), ast_strlen_zero(), 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.

{
   const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
   int penalty;

   switch ( cmd ) {
   case CLI_INIT:
      e->command = "queue add member";
      e->usage =
         "Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
         "       Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally:  a penalty, membername and a state_interface\n";
      return NULL;
   case CLI_GENERATE:
      return complete_queue_add_member(a->line, a->word, a->pos, a->n);
   }

   if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
      return CLI_SHOWUSAGE;
   } else if (strcmp(a->argv[4], "to")) {
      return CLI_SHOWUSAGE;
   } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
      return CLI_SHOWUSAGE;
   } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
      return CLI_SHOWUSAGE;
   } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
      return CLI_SHOWUSAGE;
   }

   queuename = a->argv[5];
   interface = a->argv[3];
   if (a->argc >= 8) {
      if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
         if (penalty < 0) {
            ast_cli(a->fd, "Penalty must be >= 0\n");
            penalty = 0;
         }
      } else {
         ast_cli(a->fd, "Penalty must be an integer >= 0\n");
         penalty = 0;
      }
   } else {
      penalty = 0;
   }

   if (a->argc >= 10) {
      membername = a->argv[9];
   }

   if (a->argc >= 12) {
      state_interface = a->argv[11];
   }

   switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
   case RES_OKAY:
      if (ast_strlen_zero(membername) || !log_membername_as_agent) {
         ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
      } else {
         ast_queue_log(queuename, "CLI", membername, "ADDMEMBER", "%s", "");
      }
      ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
      return CLI_SUCCESS;
   case RES_EXISTS:
      ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
      return CLI_FAILURE;
   case RES_NOSUCHQUEUE:
      ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
      return CLI_FAILURE;
   case RES_OUTOFMEMORY:
      ast_cli(a->fd, "Out of memory\n");
      return CLI_FAILURE;
   case RES_NOT_DYNAMIC:
      ast_cli(a->fd, "Member not dynamic\n");
      return CLI_FAILURE;
   default:
      return CLI_FAILURE;
   }
}
static char* handle_queue_pause_member ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

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

{
   const char *queuename, *interface, *reason;
   int paused;

   switch (cmd) {
   case CLI_INIT:
      e->command = "queue {pause|unpause} member";
      e->usage =
         "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
         "  Pause or unpause a queue member. Not specifying a particular queue\n"
         "  will pause or unpause a member across all queues to which the member\n"
         "  belongs.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
   }

   if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
      return CLI_SHOWUSAGE;
   } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
      return CLI_SHOWUSAGE;
   } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
      return CLI_SHOWUSAGE;
   }


   interface = a->argv[3];
   queuename = a->argc >= 6 ? a->argv[5] : NULL;
   reason = a->argc == 8 ? a->argv[7] : NULL;
   paused = !strcasecmp(a->argv[1], "pause");

   if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
      ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
      if (!ast_strlen_zero(queuename)) {
         ast_cli(a->fd, " in queue '%s'", queuename);
      }
      if (!ast_strlen_zero(reason)) {
         ast_cli(a->fd, " for reason '%s'", reason);
      }
      ast_cli(a->fd, "\n");
      return CLI_SUCCESS;
   } else {
      ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
      if (!ast_strlen_zero(queuename)) {
         ast_cli(a->fd, " in queue '%s'", queuename);
      }
      if (!ast_strlen_zero(reason)) {
         ast_cli(a->fd, " for reason '%s'", reason);
      }
      ast_cli(a->fd, "\n");
      return CLI_FAILURE;
   }
}
static char* handle_queue_reload ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

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

{
   struct ast_flags mask = {0,};
   int i;

   switch (cmd) {
      case CLI_INIT:
         e->command = "queue reload {parameters|members|rules|all}";
         e->usage =
            "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
            "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
            "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
            "specified in order to know what information to reload. Below is an explanation\n"
            "of each of these qualifiers.\n"
            "\n"
            "\t'members' - reload queue members from queues.conf\n"
            "\t'parameters' - reload all queue options except for queue members\n"
            "\t'rules' - reload the queuerules.conf file\n"
            "\t'all' - reload queue rules, parameters, and members\n"
            "\n"
            "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
            "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
            "one queue is specified when using this command, reloading queue rules may cause\n"
            "other queues to be affected\n";
         return NULL;
      case CLI_GENERATE:
         if (a->pos >= 3) {
            /* find the point at which the list of queue names starts */
            const char *command_end = a->line + strlen("queue reload ");
            command_end = strchr(command_end, ' ');
            if (!command_end) {
               command_end = a->line + strlen(a->line);
            }
            return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
         } else {
            return NULL;
         }
   }

   if (a->argc < 3)
      return CLI_SHOWUSAGE;

   if (!strcasecmp(a->argv[2], "rules")) {
      ast_set_flag(&mask, QUEUE_RELOAD_RULES);
   } else if (!strcasecmp(a->argv[2], "members")) {
      ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
   } else if (!strcasecmp(a->argv[2], "parameters")) {
      ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
   } else if (!strcasecmp(a->argv[2], "all")) {
      ast_set_flag(&mask, AST_FLAGS_ALL);
   }

   if (a->argc == 3) {
      reload_handler(1, &mask, NULL);
      return CLI_SUCCESS;
   }

   for (i = 3; i < a->argc; ++i) {
      reload_handler(1, &mask, a->argv[i]);
   }

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

Definition at line 9166 of file app_queue.c.

References ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_remove_member(), ast_cli_args::fd, find_member_by_queuename_and_interface(), ast_cli_args::line, member::membername, 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.

{
   const char *queuename, *interface;
   struct member *mem = NULL;
   char *res = CLI_FAILURE;

   switch (cmd) {
   case CLI_INIT:
      e->command = "queue remove member";
      e->usage =
         "Usage: queue remove member <channel> from <queue>\n"
         "       Remove a specific channel from a queue.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
   }

   if (a->argc != 6) {
      return CLI_SHOWUSAGE;
   } else if (strcmp(a->argv[4], "from")) {
      return CLI_SHOWUSAGE;
   }

   queuename = a->argv[5];
   interface = a->argv[3];

   if (log_membername_as_agent) {
      mem = find_member_by_queuename_and_interface(queuename, interface);
   }

   switch (remove_from_queue(queuename, interface)) {
   case RES_OKAY:
      if (!mem || ast_strlen_zero(mem->membername)) {
         ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
      } else {
         ast_queue_log(queuename, "CLI", mem->membername, "REMOVEMEMBER", "%s", "");
      }
      ast_cli(a->fd, "Removed interface %s from queue '%s'\n", interface, queuename);
      res = CLI_SUCCESS;
      break;
   case RES_EXISTS:
      ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
      break;
   case RES_NOSUCHQUEUE:
      ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
      break;
   case RES_OUTOFMEMORY:
      ast_cli(a->fd, "Out of memory\n");
      break;
   case RES_NOT_DYNAMIC:
      ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
      break;
   }

   if (mem) {
      ao2_ref(mem, -1);
   }

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

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

{
   struct ast_flags mask = {QUEUE_RESET_STATS,};
   int i;

   switch (cmd) {
      case CLI_INIT:
         e->command = "queue reset stats";
         e->usage =
            "Usage: queue reset stats [<queuenames>]\n"
            "\n"
            "Issuing this command will reset statistics for\n"
            "<queuenames>, or for all queues if no queue is\n"
            "specified.\n";
         return NULL;
      case CLI_GENERATE:
         if (a->pos >= 3) {
            return complete_queue(a->line, a->word, a->pos, a->n, 17);
         } else {
            return NULL;
         }
   }

   if (a->argc < 3) {
      return CLI_SHOWUSAGE;
   }

   if (a->argc == 3) {
      reload_handler(1, &mask, NULL);
      return CLI_SUCCESS;
   }

   for (i = 3; i < a->argc; ++i) {
      reload_handler(1, &mask, a->argv[i]);
   }

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

Definition at line 9444 of file app_queue.c.

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

{
   const char *rule;
   struct rule_list *rl_iter;
   struct penalty_rule *pr_iter;
   switch (cmd) {
   case CLI_INIT:
      e->command = "queue show rules";
      e->usage =
      "Usage: queue show rules [rulename]\n"
      "  Show the list of rules associated with rulename. If no\n"
      "  rulename is specified, list all rules defined in queuerules.conf\n";
      return NULL;
   case CLI_GENERATE:
      return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
   }

   if (a->argc != 3 && a->argc != 4) {
      return CLI_SHOWUSAGE;
   }

   rule = a->argc == 4 ? a->argv[3] : "";
   AST_LIST_LOCK(&rule_lists);
   AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
      if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
         ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
         AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
            ast_cli(a->fd, "\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);
         }
      }
   }
   AST_LIST_UNLOCK(&rule_lists);
   return CLI_SUCCESS;
}
static char* handle_queue_set_member_penalty ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 9381 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_value(), ast_cli_args::fd, ast_cli_args::line, MEMBER_PENALTY, ast_cli_args::n, ast_cli_args::pos, RESULT_FAILURE, RESULT_SUCCESS, set_member_value(), ast_cli_entry::usage, and ast_cli_args::word.

{
   const char *queuename = NULL, *interface;
   int penalty = 0;

   switch (cmd) {
   case CLI_INIT:
      e->command = "queue set penalty";
      e->usage =
      "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
      "  Set a member's penalty in the queue specified. If no queue is specified\n"
      "  then that interface's penalty is set in all queues to which that interface is a member\n";
      return NULL;
   case CLI_GENERATE:
      return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
   }

   if (a->argc != 6 && a->argc != 8) {
      return CLI_SHOWUSAGE;
   } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
      return CLI_SHOWUSAGE;
   }

   if (a->argc == 8) {
      queuename = a->argv[7];
   }
   interface = a->argv[5];
   penalty = atoi(a->argv[3]);

   switch (set_member_value(queuename, interface, MEMBER_PENALTY, penalty)) {
   case RESULT_SUCCESS:
      ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
      return CLI_SUCCESS;
   case RESULT_FAILURE:
      ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
      return CLI_FAILURE;
   default:
      return CLI_FAILURE;
   }
}
static char* handle_queue_set_member_ringinuse ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 9324 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_false(), ast_true(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_set_member_value(), ast_cli_args::fd, ast_cli_args::line, MEMBER_RINGINUSE, ast_cli_args::n, ast_cli_args::pos, RESULT_FAILURE, RESULT_SUCCESS, set_member_value(), ast_cli_entry::usage, and ast_cli_args::word.

{
   const char *queuename = NULL, *interface;
   int ringinuse;

   switch (cmd) {
   case CLI_INIT:
      e->command = "queue set ringinuse";
      e->usage =
      "Usage: queue set ringinuse <yes/no> on <interface> [in <queue>]\n"
      "  Set a member's ringinuse in the queue specified. If no queue is specified\n"
      "  then that interface's penalty is set in all queues to which that interface is a member.\n";
      break;
      return NULL;
   case CLI_GENERATE:
      return complete_queue_set_member_value(a->line, a->word, a->pos, a->n);
   }

   /* Sensible argument counts */
   if (a->argc != 6 && a->argc != 8) {
      return CLI_SHOWUSAGE;
   }

   /* Uses proper indicational words */
   if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
      return CLI_SHOWUSAGE;
   }

   /* Set the queue name if applicale */
   if (a->argc == 8) {
      queuename = a->argv[7];
   }

   /* Interface being set */
   interface = a->argv[5];

   /* Check and set the ringinuse value */
   if (ast_true(a->argv[3])) {
      ringinuse = 1;
   } else if (ast_false(a->argv[3])) {
      ringinuse = 0;
   } else {
      return CLI_SHOWUSAGE;
   }

   switch (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
   case RESULT_SUCCESS:
      ast_cli(a->fd, "Set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
      return CLI_SUCCESS;
   case RESULT_FAILURE:
      ast_cli(a->fd, "Failed to set ringinuse on interface '%s' from queue '%s'\n", interface, queuename);
      return CLI_FAILURE;
   default:
      return CLI_FAILURE;
   }
}
static int handle_statechange ( void *  datap) [static]

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

Definition at line 1745 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_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, ast_devstate2str(), AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_free, statechange::dev, call_queue::found, is_member_available(), call_queue::members, call_queue::name, queue_t_unref, queues, statechange::state, member::state_interface, and update_status().

Referenced by device_state_cb().

{
   struct statechange *sc = datap;
   struct ao2_iterator miter, qiter;
   struct member *m;
   struct call_queue *q;
   char interface[80], *slash_pos;
   int found = 0;       /* Found this member in any queue */
   int found_member;    /* Found this member in this queue */
   int avail = 0;       /* Found an available member in this queue */

   qiter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
      ao2_lock(q);

      avail = 0;
      found_member = 0;
      miter = ao2_iterator_init(q->members, 0);
      for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
         if (!found_member) {
            ast_copy_string(interface, m->state_interface, sizeof(interface));

            if ((slash_pos = strchr(interface, '/'))) {
               if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) {
                  *slash_pos = '\0';
               }
            }

            if (!strcasecmp(interface, sc->dev)) {
               found_member = 1;
               update_status(q, m, sc->state);
            }
         }

         /* check every member until we find one NOT_INUSE */
         if (!avail) {
            avail = is_member_available(q, m);
         }
         if (avail && found_member) {
            /* early exit as we've found an available member and the member of interest */
            ao2_ref(m, -1);
            break;
         }
      }

      if (found_member) {
         found = 1;
         if (avail) {
            ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
         } else {
            ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
         }
      }

      ao2_iterator_destroy(&miter);

      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&qiter);

   if (found) {
      ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
   } else {
      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));
   }

   ast_free(sc);
   return 0;
}
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 3354 of file app_queue.c.

References callattempt::aoc_s_rate_list, ast_aoc_destroy_decoded(), AST_CAUSE_ANSWERED_ELSEWHERE, ast_channel_hangupcause_set(), ast_hangup(), callattempt_free(), callattempt::chan, and callattempt::q_next.

Referenced by try_calling().

{
   struct callattempt *oo;

   while (outgoing) {
      /* If someone else answered the call we should indicate this in the CANCEL */
      /* Hangup any existing lines we have open */
      if (outgoing->chan && (outgoing->chan != exception)) {
         if (exception || cancel_answered_elsewhere) {
            ast_channel_hangupcause_set(outgoing->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
         }
         ast_hangup(outgoing->chan);
      }
      oo = outgoing;
      outgoing = outgoing->q_next;
      ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
      callattempt_free(oo);
   }
}
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 1999 of file app_queue.c.

References call_queue::announce_to_first_user, call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, autofill_default, call_queue::autopause, call_queue::autopausedelay, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, montype_default, call_queue::name, 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 find_queue_by_name_rt(), and reload_single_queue().

{
   int i;
   struct penalty_rule *pr_iter;

   q->dead = 0;
   q->retry = DEFAULT_RETRY;
   q->timeout = DEFAULT_TIMEOUT;
   q->maxlen = 0;
   q->announcefrequency = 0;
   q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
   q->announceholdtime = 1;
   q->announcepositionlimit = 10; /* Default 10 positions */
   q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
   q->roundingseconds = 0; /* Default - don't announce seconds */
   q->servicelevel = 0;
   q->ringinuse = 1;
   q->announce_to_first_user = 0;
   q->setinterfacevar = 0;
   q->setqueuevar = 0;
   q->setqueueentryvar = 0;
   q->autofill = autofill_default;
   q->montype = montype_default;
   q->monfmt[0] = '\0';
   q->reportholdtime = 0;
   q->wrapuptime = 0;
   q->penaltymemberslimit = 0;
   q->joinempty = 0;
   q->leavewhenempty = 0;
   q->memberdelay = 0;
   q->maskmemberstatus = 0;
   q->eventwhencalled = 0;
   q->weight = 0;
   q->timeoutrestart = 0;
   q->periodicannouncefrequency = 0;
   q->randomperiodicannounce = 0;
   q->numperiodicannounce = 0;
   q->autopause = QUEUE_AUTOPAUSE_OFF;
   q->timeoutpriority = TIMEOUT_PRIORITY_APP;
   q->autopausedelay = 0;
   if (!q->members) {
      if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED) {
         /* linear strategy depends on order, so we have to place all members in a single bucket */
         q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
      } else {
         q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
      }
   }
   q->found = 1;

   ast_string_field_set(q, sound_next, "queue-youarenext");
   ast_string_field_set(q, sound_thereare, "queue-thereare");
   ast_string_field_set(q, sound_calls, "queue-callswaiting");
   ast_string_field_set(q, queue_quantity1, "queue-quantity1");
   ast_string_field_set(q, queue_quantity2, "queue-quantity2");
   ast_string_field_set(q, sound_holdtime, "queue-holdtime");
   ast_string_field_set(q, sound_minutes, "queue-minutes");
   ast_string_field_set(q, sound_minute, "queue-minute");
   ast_string_field_set(q, sound_seconds, "queue-seconds");
   ast_string_field_set(q, sound_thanks, "queue-thankyou");
   ast_string_field_set(q, sound_reporthold, "queue-reporthold");

   if (!q->sound_periodicannounce[0]) {
      q->sound_periodicannounce[0] = ast_str_create(32);
   }

   if (q->sound_periodicannounce[0]) {
      ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
   }

   for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
      if (q->sound_periodicannounce[i]) {
         ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
      }
   }

   while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
      ast_free(pr_iter);
   }

   /* On restart assume no members are available.
    * The queue_avail hint is a boolean state to indicate whether a member is available or not.
    *
    * This seems counter intuitive, but is required to light a BLF
    * AST_DEVICE_INUSE indicates no members are available.
    * AST_DEVICE_NOT_INUSE indicates a member is available.
    */
   ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
}
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 1515 of file app_queue.c.

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

Referenced by join_queue().

{
   struct queue_ent *cur;

   if (!q || !new)
      return;
   if (prev) {
      cur = prev->next;
      prev->next = new;
   } else {
      cur = q->head;
      q->head = new;
   }
   new->next = cur;

   /* every queue_ent must have a reference to it's parent call_queue, this
    * reference does not go away until the end of the queue_ent's life, meaning
    * that even when the queue_ent leaves the call_queue this ref must remain. */
   queue_ref(q);
   new->parent = q;
   new->pos = ++(*pos);
   new->opos = *pos;
}
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:
-1on failure
0on success
Note:
Call this with the rule_lists locked

Definition at line 2118 of file app_queue.c.

References ast_calloc, ast_free, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_strlen_zero(), LOG_WARNING, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, rule_list::rules, and penalty_rule::time.

Referenced by reload_queue_rules().

{
   char *timestr, *maxstr, *minstr, *contentdup;
   struct penalty_rule *rule = NULL, *rule_iter;
   struct rule_list *rl_iter;
   int penaltychangetime, inserted = 0;

   if (!(rule = ast_calloc(1, sizeof(*rule)))) {
      return -1;
   }

   contentdup = ast_strdupa(content);

   if (!(maxstr = strchr(contentdup, ','))) {
      ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
      ast_free(rule);
      return -1;
   }

   *maxstr++ = '\0';
   timestr = contentdup;

   if ((penaltychangetime = atoi(timestr)) < 0) {
      ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
      ast_free(rule);
      return -1;
   }

   rule->time = penaltychangetime;

   if ((minstr = strchr(maxstr,','))) {
      *minstr++ = '\0';
   }

   /* The last check will evaluate true if either no penalty change is indicated for a given rule
    * OR if a min penalty change is indicated but no max penalty change is */
   if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
      rule->max_relative = 1;
   }

   rule->max_value = atoi(maxstr);

   if (!ast_strlen_zero(minstr)) {
      if (*minstr == '+' || *minstr == '-') {
         rule->min_relative = 1;
      }
      rule->min_value = atoi(minstr);
   } else { /*there was no minimum specified, so assume this means no change*/
      rule->min_relative = 1;
   }

   /*We have the rule made, now we need to insert it where it belongs*/
   AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
      if (strcasecmp(rl_iter->name, list_name)) {
         continue;
      }

      AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
         if (rule->time < rule_iter->time) {
            AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
            inserted = 1;
            break;
         }
      }
      AST_LIST_TRAVERSE_SAFE_END;

      if (!inserted) {
         AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
         inserted = 1;
      }

      break;
   }

   if (!inserted) {
      ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
      ast_free(rule);
      return -1;
   }
   return 0;
}
static const char* int2strat ( int  strategy) [static]

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

{
   int x;

   for (x = 0; x < ARRAY_LEN(strategies); x++) {
      if (strategy == strategies[x].strategy) {
         return strategies[x].name;
      }
   }

   return "<unknown>";
}
static struct member * interface_exists ( struct call_queue q,
const char *  interface 
) [static, read]

Definition at line 6001 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(), queue_function_mem_read(), queue_function_mem_write(), rna(), set_member_paused(), set_member_penalty_help_members(), and set_member_ringinuse_help_members().

{
   struct member *mem;
   struct ao2_iterator mem_iter;

   if (!q) {
      return NULL;
   }
   mem_iter = ao2_iterator_init(q->members, 0);
   while ((mem = ao2_iterator_next(&mem_iter))) {
      if (!strcasecmp(interface, mem->interface)) {
         ao2_iterator_destroy(&mem_iter);
         return mem;
      }
      ao2_ref(mem, -1);
   }
   ao2_iterator_destroy(&mem_iter);

   return NULL;
}
static int is_member_available ( struct call_queue q,
struct member mem 
) [static]

Definition at line 1712 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_RINGINUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, available(), member::lastcall, member::paused, member::ringinuse, member::status, and call_queue::wrapuptime.

Referenced by add_to_queue(), handle_statechange(), num_available_members(), and set_member_paused().

{
   int available = 0;

   switch (mem->status) {
      case AST_DEVICE_INVALID:
      case AST_DEVICE_UNAVAILABLE:
         break;
      case AST_DEVICE_INUSE:
      case AST_DEVICE_BUSY:
      case AST_DEVICE_RINGING:
      case AST_DEVICE_RINGINUSE:
      case AST_DEVICE_ONHOLD:
         if (!mem->ringinuse) {
            break;
         }
         /* else fall through */
      case AST_DEVICE_NOT_INUSE:
      case AST_DEVICE_UNKNOWN:
         if (!mem->paused) {
            available = 1;
         }
         break;
   }

   /* Let wrapuptimes override device state availability */
   if (mem->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < mem->lastcall)) {
      available = 0;
   }
   return available;
}
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]qeThe caller who wants to know if it is his turn
Return values:
0It is not our turn
1It is our turn

Definition at line 4637 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

{
   struct queue_ent *ch;
   int res;
   int avl;
   int idx = 0;
   /* This needs a lock. How many members are available to be served? */
   ao2_lock(qe->parent);

   avl = num_available_members(qe->parent);

   ch = qe->parent->head;

   ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");

   while ((idx < avl) && (ch) && (ch != qe)) {
      if (!ch->pending) {
         idx++;
      }
      ch = ch->next;
   }

   ao2_unlock(qe->parent);
   /* If the queue entry is within avl [the number of available members] calls from the top ...
    * Autofill and position check added to support autofill=no (as only calls
    * from the front of the queue are valid when autofill is disabled)
    */
   if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
      ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
      res = 1;
   } else {
      ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
      res = 0;
   }

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

Definition at line 2904 of file app_queue.c.

References queue_ent::announce, call_queue::announce, ao2_lock, ao2_unlock, ast_channel_caller(), ast_channel_connected(), ast_channel_name(), ast_channel_uniqueid(), ast_copy_string(), ast_debug, AST_DEVICE_RINGING, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_log(), ast_manager_event, queue_ent::chan, queue_ent::context, call_queue::context, call_queue::count, EVENT_FLAG_CALL, find_load_queue_rt_friendly(), get_member_status(), call_queue::head, ast_party_caller::id, ast_party_connected_line::id, insert_entry(), call_queue::joinempty, LOG_NOTICE, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, queue_ent::moh, call_queue::moh, ast_party_id::name, call_queue::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_party_name::valid, and ast_party_number::valid.

Referenced by queue_exec().

{
   struct call_queue *q;
   struct queue_ent *cur, *prev = NULL;
   int res = -1;
   int pos = 0;
   int inserted = 0;

   if (!(q = find_load_queue_rt_friendly(queuename))) {
      return res;
   }
   ao2_lock(q);

   /* This is our one */
   if (q->joinempty) {
      int status = 0;
      if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
         *reason = QUEUE_JOINEMPTY;
         ao2_unlock(q);
         queue_t_unref(q, "Done with realtime queue");
         return res;
      }
   }
   if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) {
      *reason = QUEUE_FULL;
   } else if (*reason == QUEUE_UNKNOWN) {
      /* There's space for us, put us at the right position inside
       * the queue.
       * Take into account the priority of the calling user */
      inserted = 0;
      prev = NULL;
      cur = q->head;
      while (cur) {
         /* We have higher priority than the current user, enter
          * before him, after all the other users with priority
          * higher or equal to our priority. */
         if ((!inserted) && (qe->prio > cur->prio)) {
            insert_entry(q, prev, qe, &pos);
            inserted = 1;
         }
         /* <= is necessary for the position comparison because it may not be possible to enter
          * at our desired position since higher-priority callers may have taken the position we want
          */
         if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
            insert_entry(q, prev, qe, &pos);
            inserted = 1;
            /*pos is incremented inside insert_entry, so don't need to add 1 here*/
            if (position < pos) {
               ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
            }
         }
         cur->pos = ++pos;
         prev = cur;
         cur = cur->next;
      }
      /* No luck, join at the end of the queue */
      if (!inserted) {
         insert_entry(q, prev, qe, &pos);
      }
      ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
      ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
      ast_copy_string(qe->context, q->context, sizeof(qe->context));
      q->count++;
      if (q->count == 1) {
         ast_devstate_changed(AST_DEVICE_RINGING, AST_DEVSTATE_CACHABLE, "Queue:%s", q->name);
      }

      res = 0;
      /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a channel joins a Queue.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
            <parameter name="Position">
               <para>This channel's current position in the queue.</para>
            </parameter>
            <parameter name="Count">
               <para>The total number of channels in the queue.</para>
            </parameter>
         </syntax>
         <see-also>
            <ref type="managerEvent">Leave</ref>
            <ref type="application">Queue</ref>
         </see-also>
      </managerEventInstance>
      ***/
      ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
         "Channel: %s\r\n"
         "CallerIDNum: %s\r\n"
         "CallerIDName: %s\r\n"
         "ConnectedLineNum: %s\r\n"
         "ConnectedLineName: %s\r\n"
         "Queue: %s\r\n"
         "Position: %d\r\n"
         "Count: %d\r\n"
         "Uniqueid: %s\r\n",
         ast_channel_name(qe->chan),
         S_COR(ast_channel_caller(qe->chan)->id.number.valid, ast_channel_caller(qe->chan)->id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
         S_COR(ast_channel_caller(qe->chan)->id.name.valid, ast_channel_caller(qe->chan)->id.name.str, "unknown"),
         S_COR(ast_channel_connected(qe->chan)->id.number.valid, ast_channel_connected(qe->chan)->id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
         S_COR(ast_channel_connected(qe->chan)->id.name.valid, ast_channel_connected(qe->chan)->id.name.str, "unknown"),
         q->name, qe->pos, q->count, ast_channel_uniqueid(qe->chan));
      ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, ast_channel_name(qe->chan), qe->pos );
   }
   ao2_unlock(q);
   queue_t_unref(q, "Done with realtime queue");

   return res;
}
static int kill_dead_members ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7945 of file app_queue.c.

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

Referenced by reload_single_queue().

{
   struct member *member = obj;

   if (!member->delme) {
      member->status = get_queue_member_status(member);
      return 0;
   } else {
      return CMP_MATCH;
   }
}
static int kill_dead_queues ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 8096 of file app_queue.c.

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

Referenced by reload_queues().

{
   struct call_queue *q = obj;
   char *queuename = arg;
   if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
      return CMP_MATCH;
   } else {
      return 0;
   }
}
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 3257 of file app_queue.c.

References ao2_lock, ao2_unlock, ast_channel_name(), ast_channel_uniqueid(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), 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, call_queue::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, and var.

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

{
   struct call_queue *q;
   struct queue_ent *current, *prev = NULL;
   struct penalty_rule *pr_iter;
   int pos = 0;

   if (!(q = qe->parent)) {
      return;
   }
   queue_t_ref(q, "Copy queue pointer from queue entry");
   ao2_lock(q);

   prev = NULL;
   for (current = q->head; current; current = current->next) {
      if (current == qe) {
         char posstr[20];
         q->count--;
         if (!q->count) {
            ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s", q->name);
         }

         /* Take us out of the queue */
         /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when a channel leaves a Queue.</synopsis>
            <syntax>
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Join']/managerEventInstance/syntax/parameter[@name='Count'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Join']/managerEventInstance/syntax/parameter[@name='Position'])" />
            </syntax>
            <see-also>
               <ref type="managerEvent">Join</ref>
            </see-also>
         </managerEventInstance>
         ***/
         ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
            "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
            ast_channel_name(qe->chan), q->name,  q->count, qe->pos, ast_channel_uniqueid(qe->chan));
         ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
         /* Take us out of the queue */
         if (prev) {
            prev->next = current->next;
         } else {
            q->head = current->next;
         }
         /* Free penalty rules */
         while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
            ast_free(pr_iter);
         }
         snprintf(posstr, sizeof(posstr), "%d", qe->pos);
         pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
      } else {
         /* Renumber the people after us in the queue based on a new count */
         current->pos = ++pos;
         prev = current;
      }
   }
   ao2_unlock(q);

   /*If the queue is a realtime queue, check to see if it's still defined in real time*/
   if (q->realtime) {
      struct ast_variable *var;
      if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
         q->dead = 1;
      } else {
         ast_variables_destroy(var);
      }
   }

   if (q->dead) {
      /* It's dead and nobody is in it, so kill it */
      queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
   }
   /* unref the explicit ref earlier in the function */
   queue_t_unref(q, "Expire copied reference");
}
static int load_module ( void  ) [static]

Definition at line 9926 of file app_queue.c.

References ao2_container_alloc, aqm_exec(), ARRAY_LEN, ast_cli_register_multiple(), ast_config_destroy(), 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_load_realtime_multientry(), ast_log(), ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application_xml, ast_taskprocessor_get(), ast_variable_retrieve(), cli_queue, device_state_cb(), EVENT_FLAG_AGENT, extension_state_cb(), LOG_NOTICE, LOG_WARNING, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_log_custom(), manager_queue_member_penalty(), manager_queue_member_ringinuse(), 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().

{
   int res;
   struct ast_flags mask = {AST_FLAGS_ALL, };
   struct ast_config *member_config;

   queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);

   use_weight = 0;

   if (reload_handler(0, &mask, NULL))
      return AST_MODULE_LOAD_DECLINE;

   ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);

   /*
    * This section is used to determine which name for 'ringinuse' to use in realtime members
    * Necessary for supporting older setups.
    */
   member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
   if (!member_config) {
      realtime_ringinuse_field = "ringinuse";
   } else {
      const char *config_val;
      if ((config_val = ast_variable_retrieve(member_config, NULL, "ringinuse"))) {
         ast_log(LOG_NOTICE, "ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
         realtime_ringinuse_field = "ringinuse";
      } else if ((config_val = ast_variable_retrieve(member_config, NULL, "ignorebusy"))) {
         ast_log(LOG_NOTICE, "ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
         realtime_ringinuse_field = "ignorebusy";
      } else {
         ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
         realtime_ringinuse_field = "ringinuse";
      }
   }

   ast_config_destroy(member_config);

   if (queue_persistent_members)
      reload_queue_members();

   ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));

   ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
   res = ast_register_application_xml(app, queue_exec);
   res |= ast_register_application_xml(app_aqm, aqm_exec);
   res |= ast_register_application_xml(app_rqm, rqm_exec);
   res |= ast_register_application_xml(app_pqm, pqm_exec);
   res |= ast_register_application_xml(app_upqm, upqm_exec);
   res |= ast_register_application_xml(app_ql, ql_exec);
   res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
   res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
   res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
   res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
   res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
   res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
   res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
   res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
   res |= ast_manager_register_xml("QueueMemberRingInUse", EVENT_FLAG_AGENT, manager_queue_member_ringinuse);
   res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
   res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
   res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
   res |= ast_custom_function_register(&queuevar_function);
   res |= ast_custom_function_register(&queueexists_function);
   res |= ast_custom_function_register(&queuemembercount_function);
   res |= ast_custom_function_register(&queuemembercount_dep);
   res |= ast_custom_function_register(&queuememberlist_function);
   res |= ast_custom_function_register(&queuewaitingcount_function);
   res |= ast_custom_function_register(&queuememberpenalty_function);

   if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
      ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
   }

   /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
   if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
      res = -1;
   }

   ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);

   return res ? AST_MODULE_LOAD_DECLINE : 0;
}
static int manager_add_queue_member ( struct mansession s,
const struct message m 
) [static]

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

{
   const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
   int paused, penalty = 0;

   queuename = astman_get_header(m, "Queue");
   interface = astman_get_header(m, "Interface");
   penalty_s = astman_get_header(m, "Penalty");
   paused_s = astman_get_header(m, "Paused");
   membername = astman_get_header(m, "MemberName");
   state_interface = astman_get_header(m, "StateInterface");

   if (ast_strlen_zero(queuename)) {
      astman_send_error(s, m, "'Queue' not specified.");
      return 0;
   }

   if (ast_strlen_zero(interface)) {
      astman_send_error(s, m, "'Interface' not specified.");
      return 0;
   }

   if (ast_strlen_zero(penalty_s)) {
      penalty = 0;
   } else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) {
      penalty = 0;
   }

   if (ast_strlen_zero(paused_s)) {
      paused = 0;
   } else {
      paused = abs(ast_true(paused_s));
   }

   switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
   case RES_OKAY:
      if (ast_strlen_zero(membername) || !log_membername_as_agent) {
         ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
      } else {
         ast_queue_log(queuename, "MANAGER", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
      }
      astman_send_ack(s, m, "Added interface to queue");
      break;
   case RES_EXISTS:
      astman_send_error(s, m, "Unable to add interface: Already there");
      break;
   case RES_NOSUCHQUEUE:
      astman_send_error(s, m, "Unable to add interface to queue: No such queue");
      break;
   case RES_OUTOFMEMORY:
      astman_send_error(s, m, "Out of memory");
      break;
   }

   return 0;
}
static int manager_pause_queue_member ( struct mansession s,
const struct message m 
) [static]

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

{
   const char *queuename, *interface, *paused_s, *reason;
   int paused;

   interface = astman_get_header(m, "Interface");
   paused_s = astman_get_header(m, "Paused");
   queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
   reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */

   if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
      astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
      return 0;
   }

   paused = abs(ast_true(paused_s));

   if (set_member_paused(queuename, interface, reason, paused)) {
      astman_send_error(s, m, "Interface not found");
   } else {
      astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
   }
   return 0;
}
static int manager_queue_log_custom ( struct mansession s,
const struct message m 
) [static]

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

{
   const char *queuename, *event, *message, *interface, *uniqueid;

   queuename = astman_get_header(m, "Queue");
   uniqueid = astman_get_header(m, "UniqueId");
   interface = astman_get_header(m, "Interface");
   event = astman_get_header(m, "Event");
   message = astman_get_header(m, "Message");

   if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
      astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
      return 0;
   }

   ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
   astman_send_ack(s, m, "Event added successfully");

   return 0;
}
static int manager_queue_member_penalty ( struct mansession s,
const struct message m 
) [static]

Definition at line 9014 of file app_queue.c.

References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), MEMBER_PENALTY, and set_member_value().

Referenced by load_module().

{
   const char *queuename, *interface, *penalty_s;
   int penalty;

   interface = astman_get_header(m, "Interface");
   penalty_s = astman_get_header(m, "Penalty");
   /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
   queuename = astman_get_header(m, "Queue");

   if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
      astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
      return 0;
   }

   penalty = atoi(penalty_s);

   if (set_member_value((char *)queuename, (char *)interface, MEMBER_PENALTY, penalty)) {
      astman_send_error(s, m, "Invalid interface, queuename or penalty");
   } else {
      astman_send_ack(s, m, "Interface penalty set successfully");
   }

   return 0;
}
static int manager_queue_member_ringinuse ( struct mansession s,
const struct message m 
) [static]

Definition at line 8980 of file app_queue.c.

References ast_false(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), MEMBER_RINGINUSE, and set_member_value().

Referenced by load_module().

{
   const char *queuename, *interface, *ringinuse_s;
   int ringinuse;

   interface = astman_get_header(m, "Interface");
   ringinuse_s = astman_get_header(m, "RingInUse");

   /* Optional - if not supplied, set the ringinuse value for the given Interface in all queues */
   queuename = astman_get_header(m, "Queue");

   if (ast_strlen_zero(interface) || ast_strlen_zero(ringinuse_s)) {
      astman_send_error(s, m, "Need 'Interface' and 'RingInUse' parameters.");
      return 0;
   }

   if (ast_true(ringinuse_s)) {
      ringinuse = 1;
   } else if (ast_false(ringinuse_s)) {
      ringinuse = 0;
   } else {
      astman_send_error(s, m, "'RingInUse' parameter must be a truth value (yes/no, on/off, 0/1, etc)");
      return 0;
   }

   if (set_member_value(queuename, interface, MEMBER_RINGINUSE, ringinuse)) {
      astman_send_error(s, m, "Invalid interface, queuename, or ringinuse value\n");
   } else {
      astman_send_ack(s, m, "Interface ringinuse set successfully");
   }

   return 0;
}
static int manager_queue_reload ( struct mansession s,
const struct message m 
) [static]

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

{
   struct ast_flags mask = {0,};
   const char *queuename = NULL;
   int header_found = 0;

   queuename = astman_get_header(m, "Queue");
   if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
      ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
      header_found = 1;
   }
   if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
      ast_set_flag(&mask, QUEUE_RELOAD_RULES);
      header_found = 1;
   }
   if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
      ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
      header_found = 1;
   }

   if (!header_found) {
      ast_set_flag(&mask, AST_FLAGS_ALL);
   }

   if (!reload_handler(1, &mask, queuename)) {
      astman_send_ack(s, m, "Queue reloaded successfully");
   } else {
      astman_send_error(s, m, "Error encountered while reloading queue");
   }
   return 0;
}
static int manager_queue_reset ( struct mansession s,
const struct message m 
) [static]

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

{
   const char *queuename = NULL;
   struct ast_flags mask = {QUEUE_RESET_STATS,};

   queuename = astman_get_header(m, "Queue");

   if (!reload_handler(1, &mask, queuename)) {
      astman_send_ack(s, m, "Queue stats reset successfully");
   } else {
      astman_send_error(s, m, "Error encountered while resetting queue stats");
   }
   return 0;
}
static int manager_queue_rule_show ( struct mansession s,
const struct message m 
) [static]

Definition at line 8537 of file app_queue.c.

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

Referenced by load_module().

{
   const char *rule = astman_get_header(m, "Rule");
   const char *id = astman_get_header(m, "ActionID");
   struct rule_list *rl_iter;
   struct penalty_rule *pr_iter;

   astman_append(s, "Response: Success\r\n");
   if (!ast_strlen_zero(id)) {
      astman_append(s, "ActionID: %s\r\n", id);
   }

   AST_LIST_LOCK(&rule_lists);
   AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
      if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
         astman_append(s, "RuleList: %s\r\n", rl_iter->name);
         AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
            astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
         }
         if (!ast_strlen_zero(rule)) {
            break;
         }
      }
   }
   AST_LIST_UNLOCK(&rule_lists);

   /*
    * Two blank lines instead of one because the Response and
    * ActionID headers used to not be present.
    */
   astman_append(s, "\r\n\r\n");

   return RESULT_SUCCESS;
}
static int manager_queues_show ( struct mansession s,
const struct message m 
) [static]

Definition at line 8527 of file app_queue.c.

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

Referenced by load_module().

{
   static const char * const a[] = { "queue", "show" };

   __queues_show(s, -1, 2, a);
   astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */

   return RESULT_SUCCESS;
}
static int manager_queues_status ( struct mansession s,
const struct message m 
) [static]

Queue status info via AMI.

Definition at line 8649 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_channel_caller(), ast_channel_connected(), ast_channel_name(), ast_channel_uniqueid(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, ast_party_caller::id, ast_party_connected_line::id, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, ast_party_id::name, call_queue::name, queue_ent::next, ast_party_id::number, member::paused, member::penalty, queue_ent::pos, queue_t_unref, queues, RESULT_SUCCESS, S_COR, call_queue::servicelevel, queue_ent::start, member::state_interface, member::status, ast_party_name::str, ast_party_number::str, call_queue::strategy, call_queue::talktime, ast_party_name::valid, ast_party_number::valid, and call_queue::weight.

Referenced by load_module().

{
   time_t now;
   int pos;
   const char *id = astman_get_header(m,"ActionID");
   const char *queuefilter = astman_get_header(m,"Queue");
   const char *memberfilter = astman_get_header(m,"Member");
   char idText[256] = "";
   struct call_queue *q;
   struct queue_ent *qe;
   float sl = 0;
   struct member *mem;
   struct ao2_iterator queue_iter;
   struct ao2_iterator mem_iter;

   astman_send_ack(s, m, "Queue status will follow");
   time(&now);
   if (!ast_strlen_zero(id)) {
      snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
   }

   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      ao2_lock(q);

      /* List queue properties */
      if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
         sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
         astman_append(s, "Event: QueueParams\r\n"
            "Queue: %s\r\n"
            "Max: %d\r\n"
            "Strategy: %s\r\n"
            "Calls: %d\r\n"
            "Holdtime: %d\r\n"
            "TalkTime: %d\r\n"
            "Completed: %d\r\n"
            "Abandoned: %d\r\n"
            "ServiceLevel: %d\r\n"
            "ServicelevelPerf: %2.1f\r\n"
            "Weight: %d\r\n"
            "%s"
            "\r\n",
            q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
            q->callsabandoned, q->servicelevel, sl, q->weight, idText);
         /* List Queue Members */
         mem_iter = ao2_iterator_init(q->members, 0);
         while ((mem = ao2_iterator_next(&mem_iter))) {
            if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
               astman_append(s, "Event: QueueMember\r\n"
                  "Queue: %s\r\n"
                  "Name: %s\r\n"
                  "Location: %s\r\n"
                  "StateInterface: %s\r\n"
                  "Membership: %s\r\n"
                  "Penalty: %d\r\n"
                  "CallsTaken: %d\r\n"
                  "LastCall: %d\r\n"
                  "Status: %d\r\n"
                  "Paused: %d\r\n"
                  "%s"
                  "\r\n",
                  q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
                  mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
            }
            ao2_ref(mem, -1);
         }
         ao2_iterator_destroy(&mem_iter);
         /* List Queue Entries */
         pos = 1;
         for (qe = q->head; qe; qe = qe->next) {
            astman_append(s, "Event: QueueEntry\r\n"
               "Queue: %s\r\n"
               "Position: %d\r\n"
               "Channel: %s\r\n"
               "Uniqueid: %s\r\n"
               "CallerIDNum: %s\r\n"
               "CallerIDName: %s\r\n"
               "ConnectedLineNum: %s\r\n"
               "ConnectedLineName: %s\r\n"
               "Wait: %ld\r\n"
               "%s"
               "\r\n",
               q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
               S_COR(ast_channel_caller(qe->chan)->id.number.valid, ast_channel_caller(qe->chan)->id.number.str, "unknown"),
               S_COR(ast_channel_caller(qe->chan)->id.name.valid, ast_channel_caller(qe->chan)->id.name.str, "unknown"),
               S_COR(ast_channel_connected(qe->chan)->id.number.valid, ast_channel_connected(qe->chan)->id.number.str, "unknown"),
               S_COR(ast_channel_connected(qe->chan)->id.name.valid, ast_channel_connected(qe->chan)->id.name.str, "unknown"),
               (long) (now - qe->start), idText);
         }
      }
      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&queue_iter);

   astman_append(s,
      "Event: QueueStatusComplete\r\n"
      "%s"
      "\r\n",idText);

   return RESULT_SUCCESS;
}
static int manager_queues_summary ( struct mansession s,
const struct message m 
) [static]

Summary of queue info via the AMI.

Definition at line 8573 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_UNAVAILABLE, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), call_queue::head, call_queue::holdtime, member_status_available(), 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().

{
   time_t now;
   int qmemcount = 0;
   int qmemavail = 0;
   int qchancount = 0;
   int qlongestholdtime = 0;
   const char *id = astman_get_header(m, "ActionID");
   const char *queuefilter = astman_get_header(m, "Queue");
   char idText[256] = "";
   struct call_queue *q;
   struct queue_ent *qe;
   struct member *mem;
   struct ao2_iterator queue_iter;
   struct ao2_iterator mem_iter;

   astman_send_ack(s, m, "Queue summary will follow");
   time(&now);
   if (!ast_strlen_zero(id)) {
      snprintf(idText, 256, "ActionID: %s\r\n", id);
   }
   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
      ao2_lock(q);

      /* List queue properties */
      if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
         /* Reset the necessary local variables if no queuefilter is set*/
         qmemcount = 0;
         qmemavail = 0;
         qchancount = 0;
         qlongestholdtime = 0;

         /* List Queue Members */
         mem_iter = ao2_iterator_init(q->members, 0);
         while ((mem = ao2_iterator_next(&mem_iter))) {
            if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
               ++qmemcount;
               if (member_status_available(mem->status) && !mem->paused) {
                  ++qmemavail;
               }
            }
            ao2_ref(mem, -1);
         }
         ao2_iterator_destroy(&mem_iter);
         for (qe = q->head; qe; qe = qe->next) {
            if ((now - qe->start) > qlongestholdtime) {
               qlongestholdtime = now - qe->start;
            }
            ++qchancount;
         }
         astman_append(s, "Event: QueueSummary\r\n"
            "Queue: %s\r\n"
            "LoggedIn: %d\r\n"
            "Available: %d\r\n"
            "Callers: %d\r\n"
            "HoldTime: %d\r\n"
            "TalkTime: %d\r\n"
            "LongestHoldTime: %d\r\n"
            "%s"
            "\r\n",
            q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
      }
      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&queue_iter);
   astman_append(s,
      "Event: QueueSummaryComplete\r\n"
      "%s"
      "\r\n", idText);

   return RESULT_SUCCESS;
}
static int manager_remove_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 8809 of file app_queue.c.

References ao2_ref, ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), find_member_by_queuename_and_interface(), member::membername, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

{
   const char *queuename, *interface;
   struct member *mem = NULL;

   queuename = astman_get_header(m, "Queue");
   interface = astman_get_header(m, "Interface");

   if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
      astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
      return 0;
   }

   if (log_membername_as_agent) {
      mem = find_member_by_queuename_and_interface(queuename, interface);
   }

   switch (remove_from_queue(queuename, interface)) {
   case RES_OKAY:
      if (!mem || ast_strlen_zero(mem->membername)) {
         ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
      } else {
         ast_queue_log(queuename, "MANAGER", mem->membername, "REMOVEMEMBER", "%s", "");
      }
      astman_send_ack(s, m, "Removed interface from queue");
      break;
   case RES_EXISTS:
      astman_send_error(s, m, "Unable to remove interface: Not there");
      break;
   case RES_NOSUCHQUEUE:
      astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
      break;
   case RES_OUTOFMEMORY:
      astman_send_error(s, m, "Out of memory");
      break;
   case RES_NOT_DYNAMIC:
      astman_send_error(s, m, "Member not dynamic");
      break;
   }

   if (mem) {
      ao2_ref(mem, -1);
   }

   return 0;
}
static int mark_dead_and_unfound ( void *  obj,
void *  arg,
int  flags 
) [static]

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

{
   struct call_queue *q = obj;
   char *queuename = arg;
   if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
      q->dead = 1;
      q->found = 0;
   }
   return 0;
}
static int mark_member_dead ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 7936 of file app_queue.c.

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

Referenced by reload_single_queue().

{
   struct member *member = obj;
   if (!member->dynamic) {
      member->delme = 1;
   }
   return 0;
}
static void member_add_to_queue ( struct call_queue queue,
struct member mem 
) [static]
static void member_call_pending_clear ( struct member mem) [static]

Definition at line 3518 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry(), and ring_entry().

{
   ao2_lock(mem);
   mem->call_pending = 0;
   ao2_unlock(mem);
}
static int member_call_pending_set ( struct member mem) [static]

Definition at line 3533 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry().

{
   int old_pending;

   ao2_lock(mem);
   old_pending = mem->call_pending;
   mem->call_pending = 1;
   ao2_unlock(mem);

   return old_pending;
}
static int member_cmp_fn ( void *  obj1,
void *  obj2,
int  flags 
) [static]

Definition at line 1986 of file app_queue.c.

References CMP_MATCH, CMP_STOP, member::interface, and OBJ_KEY.

Referenced by init_queue().

{
   struct member *mem1 = obj1;
   struct member *mem2 = obj2;
   const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;

   return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
}
static int member_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 1970 of file app_queue.c.

References compress_char(), member::interface, and OBJ_KEY.

Referenced by init_queue().

{
   const struct member *mem = obj;
   const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
   const char *chname = strchr(interface, '/');
   int ret = 0, i;

   if (!chname) {
      chname = interface;
   }
   for (i = 0; i < 5 && chname[i]; i++) {
      ret += compress_char(chname[i]) << (i * 6);
   }
   return ret;
}
static void member_remove_from_queue ( struct call_queue queue,
struct member mem 
) [static]
static int member_status_available ( int  status) [static]

Definition at line 3505 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.

Referenced by can_ring_entry(), and manager_queues_summary().

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]qThe queue for which we are couting the number of available members
Returns:
Return the number of available members in queue q

Definition at line 3382 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, call_queue::autofill, is_member_available(), call_queue::members, QUEUE_STRATEGY_RINGALL, and call_queue::strategy.

Referenced by compare_weight(), is_our_turn(), remove_from_queue(), and set_member_paused().

{
   struct member *mem;
   int avl = 0;
   struct ao2_iterator mem_iter;

   mem_iter = ao2_iterator_init(q->members, 0);
   while ((mem = ao2_iterator_next(&mem_iter))) {

      avl += is_member_available(q, mem);
      ao2_ref(mem, -1);

      /* If autofill is not enabled or if the queue's strategy is ringall, then
       * we really don't care about the number of available members so much as we
       * do that there is at least one available.
       *
       * In fact, we purposely will return from this function stating that only
       * one member is available if either of those conditions hold. That way,
       * functions which determine what action to take based on the number of available
       * members will operate properly. The reasoning is that even if multiple
       * members are available, only the head caller can actually be serviced.
       */
      if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
         break;
      }
   }
   ao2_iterator_destroy(&mem_iter);

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

Definition at line 2200 of file app_queue.c.

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

Referenced by queue_set_param().

{
   char *value_copy = ast_strdupa(value);
   char *option = NULL;
   while ((option = strsep(&value_copy, ","))) {
      if (!strcasecmp(option, "paused")) {
         *empty |= QUEUE_EMPTY_PAUSED;
      } else if (!strcasecmp(option, "penalty")) {
         *empty |= QUEUE_EMPTY_PENALTY;
      } else if (!strcasecmp(option, "inuse")) {
         *empty |= QUEUE_EMPTY_INUSE;
      } else if (!strcasecmp(option, "ringing")) {
         *empty |= QUEUE_EMPTY_RINGING;
      } else if (!strcasecmp(option, "invalid")) {
         *empty |= QUEUE_EMPTY_INVALID;
      } else if (!strcasecmp(option, "wrapup")) {
         *empty |= QUEUE_EMPTY_WRAPUP;
      } else if (!strcasecmp(option, "unavailable")) {
         *empty |= QUEUE_EMPTY_UNAVAILABLE;
      } else if (!strcasecmp(option, "unknown")) {
         *empty |= QUEUE_EMPTY_UNKNOWN;
      } else if (!strcasecmp(option, "loose")) {
         *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
      } else if (!strcasecmp(option, "strict")) {
         *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
      } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
         *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
      } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
         *empty = 0;
      } else {
         ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
      }
   }
}
static int play_file ( struct ast_channel chan,
const char *  filename 
) [static]

Definition at line 3014 of file app_queue.c.

References ast_channel_language(), AST_DIGIT_ANY, ast_fileexists(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), and ast_waitstream().

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

{
   int res;

   if (ast_strlen_zero(filename)) {
      return 0;
   }

   if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
      return 0;
   }

   ast_stopstream(chan);

   res = ast_streamfile(chan, filename, ast_channel_language(chan));
   if (!res) {
      res = ast_waitstream(chan, AST_DIGIT_ANY);
   }

   ast_stopstream(chan);

   return res;
}
static int pqm_exec ( struct ast_channel chan,
const char *  data 
) [static]

PauseQueueMember application.

Definition at line 6655 of file app_queue.c.

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

Referenced by load_module().

{
   char *parse;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(interface);
      AST_APP_ARG(options);
      AST_APP_ARG(reason);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (ast_strlen_zero(args.interface)) {
      ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
      return -1;
   }

   if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
      ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
      pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
      return 0;
   }

   pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");

   return 0;
}
static int ql_exec ( struct ast_channel chan,
const char *  data 
) [static]

QueueLog application.

Definition at line 6866 of file app_queue.c.

References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, member::membername, and parse().

Referenced by load_module().

{
   char *parse;

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(uniqueid);
      AST_APP_ARG(membername);
      AST_APP_ARG(event);
      AST_APP_ARG(params);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
       || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
      ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
      return -1;
   }

   ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event,
      "%s", args.params ? args.params : "");

   return 0;
}
static int queue_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 1388 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

{
   struct call_queue *q = obj, *q2 = arg;
   return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
}
static int queue_delme_members_decrement_followers ( void *  obj,
void *  arg,
int  flag 
) [static]

Definition at line 1417 of file app_queue.c.

References ao2_callback, member::delme, call_queue::members, OBJ_MULTIPLE, OBJ_NODATA, queue_member_decrement_followers(), member::queuepos, and call_queue::rrpos.

Referenced by reload_single_queue().

{
   struct member *mem = obj;
   struct call_queue *queue = arg;
   int rrpos = mem->queuepos;

   if (mem->delme) {
      ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &rrpos);
   }

   return 0;
}
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 6941 of file app_queue.c.

References call_queue::announcefrequency, ao2_container_count(), args, AST_APP_ARG, ast_app_parse_options(), ast_assert, ast_channel_caller(), ast_channel_lock, ast_channel_name(), ast_channel_uniqueid(), ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_FIRST, ast_log(), ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strlen_zero(), ast_test_flag, ast_verb, queue_ent::chan, copy_rules(), queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::members, queue_ent::min_penalty, queue_ent::moh, call_queue::name, queue_ent::opos, OPT_ARG_ARRAY_SIZE, OPT_GO_ON, OPT_RING_WHEN_RINGING, OPT_RINGING, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, QUEUE_CONTINUE, queue_exec_options, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, queue_unref(), record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), set_queue_variables(), queue_ent::start, status, stop, penalty_rule::time, try_calling(), update_qe_rule(), update_realtime_members(), url, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

{
   int res=-1;
   int ringing=0;
   const char *user_priority;
   const char *max_penalty_str;
   const char *min_penalty_str;
   int prio;
   int qcontinue = 0;
   int max_penalty, min_penalty;
   enum queue_result reason = QUEUE_UNKNOWN;
   /* whether to exit Queue application after the timeout hits */
   int tries = 0;
   int noption = 0;
   char *parse;
   int makeannouncement = 0;
   int position = 0;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(options);
      AST_APP_ARG(url);
      AST_APP_ARG(announceoverride);
      AST_APP_ARG(queuetimeoutstr);
      AST_APP_ARG(agi);
      AST_APP_ARG(macro);
      AST_APP_ARG(gosub);
      AST_APP_ARG(rule);
      AST_APP_ARG(position);
   );
   /* Our queue entry */
   struct queue_ent qe = { 0 };
   struct ast_flags opts = { 0, };
   char *opt_args[OPT_ARG_ARRAY_SIZE];

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
      return -1;
   }

   parse = ast_strdupa(data);
   AST_STANDARD_APP_ARGS(args, parse);

   ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, macro: %s, gosub: %s, rule: %s, position: %s\n",
      args.queuename,
      S_OR(args.options, ""),
      S_OR(args.url, ""),
      S_OR(args.announceoverride, ""),
      S_OR(args.queuetimeoutstr, ""),
      S_OR(args.agi, ""),
      S_OR(args.macro, ""),
      S_OR(args.gosub, ""),
      S_OR(args.rule, ""),
      S_OR(args.position, ""));

   if (!ast_strlen_zero(args.options)) {
      ast_app_parse_options(queue_exec_options, &opts, opt_args, args.options);
   }

   /* Setup our queue entry */
   qe.start = time(NULL);

   /* set the expire time based on the supplied timeout; */
   if (!ast_strlen_zero(args.queuetimeoutstr)) {
      qe.expire = qe.start + atoi(args.queuetimeoutstr);
   } else {
      qe.expire = 0;
   }

   /* Get the priority from the variable ${QUEUE_PRIO} */
   ast_channel_lock(chan);
   user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
   if (user_priority) {
      if (sscanf(user_priority, "%30d", &prio) == 1) {
         ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
      } else {
         ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
            user_priority, ast_channel_name(chan));
         prio = 0;
      }
   } else {
      ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
      prio = 0;
   }

   /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */

   if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
      if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
         ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
      } else {
         ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
            max_penalty_str, ast_channel_name(chan));
         max_penalty = INT_MAX;
      }
   } else {
      max_penalty = INT_MAX;
   }

   if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
      if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
         ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
      } else {
         ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
            min_penalty_str, ast_channel_name(chan));
         min_penalty = INT_MAX;
      }
   } else {
      min_penalty = INT_MAX;
   }
   ast_channel_unlock(chan);

   if (ast_test_flag(&opts, OPT_RINGING)) {
      ringing = 1;
   }

   if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
      qe.ring_when_ringing = 1;
   }

   if (ast_test_flag(&opts, OPT_GO_ON)) {
      qcontinue = 1;
   }

   if (args.position) {
      position = atoi(args.position);
      if (position < 0) {
         ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
         position = 0;
      }
   }

   ast_debug(1, "queue: %s, expires: %ld, priority: %d\n",
      args.queuename, (long)qe.expire, prio);

   qe.chan = chan;
   qe.prio = prio;
   qe.max_penalty = max_penalty;
   qe.min_penalty = min_penalty;
   qe.last_pos_said = 0;
   qe.last_pos = 0;
   qe.last_periodic_announce_time = time(NULL);
   qe.last_periodic_announce_sound = 0;
   qe.valid_digits = 0;
   if (join_queue(args.queuename, &qe, &reason, position)) {
      ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
      set_queue_result(chan, reason);
      return 0;
   }
   ast_assert(qe.parent != NULL);

   ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
      S_OR(args.url, ""),
      S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
      qe.opos);
   copy_rules(&qe, args.rule);
   qe.pr = AST_LIST_FIRST(&qe.qe_rules);
check_turns:
   if (ringing) {
      ast_indicate(chan, AST_CONTROL_RINGING);
   } else {
      ast_moh_start(chan, qe.moh, NULL);
   }

   /* This is the wait loop for callers 2 through maxlen */
   res = wait_our_turn(&qe, ringing, &reason);
   if (res) {
      goto stop;
   }

   makeannouncement = 0;

   for (;;) {
      /* This is the wait loop for the head caller*/
      /* To exit, they may get their call answered; */
      /* they may dial a digit from the queue context; */
      /* or, they may timeout. */

      /* Leave if we have exceeded our queuetimeout */
      if (qe.expire && (time(NULL) >= qe.expire)) {
         record_abandoned(&qe);
         reason = QUEUE_TIMEOUT;
         res = 0;
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
            qe.pos, qe.opos, (long) time(NULL) - qe.start);
         break;
      }

      if (makeannouncement) {
         /* Make a position announcement, if enabled */
         if (qe.parent->announcefrequency)
            if ((res = say_position(&qe,ringing)))
               goto stop;
      }
      makeannouncement = 1;

      /* Make a periodic announcement, if enabled */
      if (qe.parent->periodicannouncefrequency) {
         if ((res = say_periodic_announcement(&qe,ringing))) {
            goto stop;
         }
      }

      /* Leave if we have exceeded our queuetimeout */
      if (qe.expire && (time(NULL) >= qe.expire)) {
         record_abandoned(&qe);
         reason = QUEUE_TIMEOUT;
         res = 0;
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
         break;
      }

      /* see if we need to move to the next penalty level for this queue */
      while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
         update_qe_rule(&qe);
      }

      /* Try calling all queue members for 'timeout' seconds */
      res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
      if (res) {
         goto stop;
      }

      if (qe.parent->leavewhenempty) {
         int status = 0;
         if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
            record_abandoned(&qe);
            reason = QUEUE_LEAVEEMPTY;
            ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
            res = 0;
            break;
         }
      }

      /* exit after 'timeout' cycle if 'n' option enabled */
      if (noption && tries >= ao2_container_count(qe.parent->members)) {
         ast_verb(3, "Exiting on time-out cycle\n");
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
         record_abandoned(&qe);
         reason = QUEUE_TIMEOUT;
         res = 0;
         break;
      }


      /* Leave if we have exceeded our queuetimeout */
      if (qe.expire && (time(NULL) >= qe.expire)) {
         record_abandoned(&qe);
         reason = QUEUE_TIMEOUT;
         res = 0;
         ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
         break;
      }

      /* If using dynamic realtime members, we should regenerate the member list for this queue */
      update_realtime_members(qe.parent);
      /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
      res = wait_a_bit(&qe);
      if (res) {
         goto stop;
      }

      /* Since this is a priority queue and
       * it is not sure that we are still at the head
       * of the queue, go and check for our turn again.
       */
      if (!is_our_turn(&qe)) {
         ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
         goto check_turns;
      }
   }

stop:
   if (res) {
      if (res < 0) {
         if (!qe.handled) {
            record_abandoned(&qe);
            ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
               "%d|%d|%ld", qe.pos, qe.opos,
               (long) time(NULL) - qe.start);
            res = -1;
         } else if (qcontinue) {
            reason = QUEUE_CONTINUE;
            res = 0;
         }
      } else if (qe.valid_digits) {
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
            "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) time(NULL) - qe.start);
      }
   }

   /* Don't allow return code > 0 */
   if (res >= 0) {
      res = 0;
      if (ringing) {
         ast_indicate(chan, -1);
      } else {
         ast_moh_stop(chan);
      }
      ast_stopstream(chan);
   }

   set_queue_variables(qe.parent, qe.chan);

   leave_queue(&qe);
   if (reason != QUEUE_UNKNOWN)
      set_queue_result(chan, reason);

   /*
    * every queue_ent is given a reference to it's parent
    * call_queue when it joins the queue.  This ref must be taken
    * away right before the queue_ent is destroyed.  In this case
    * the queue_ent is about to be returned on the stack
    */
   qe.parent = queue_unref(qe.parent);

   return res;
}
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 7311 of file app_queue.c.

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

{
   struct call_queue *q;

   buf[0] = '\0';

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
      return -1;
   }
   q = find_load_queue_rt_friendly(data);
   snprintf(buf, len, "%d", q != NULL? 1 : 0);
   if (q) {
      queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
   }

   return 0;
}
static int queue_function_mem_read ( 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.

Get or set member properties penalty / paused / ringinuse

Return values:
numberof members (busy / free / ready / total) or member info (penalty / paused / ringinuse)
-1on error

Definition at line 7336 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, args, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), call_queue::count, find_load_queue_rt_friendly(), interface_exists(), member::lastcall, LOG_ERROR, LOG_WARNING, call_queue::members, member::paused, member::penalty, queue_t_unref, member::ringinuse, member::status, and call_queue::wrapuptime.

{
   int count = 0;
   struct member *m;
   struct ao2_iterator mem_iter;
   struct call_queue *q;

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(option);
      AST_APP_ARG(interface);
   );
   /* Make sure the returned value on error is zero length string. */
   buf[0] = '\0';

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
      return -1;
   }

   AST_STANDARD_APP_ARGS(args, data);

   if (args.argc < 2) {
      ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
      return -1;
   }

   if ((q = find_load_queue_rt_friendly(args.queuename))) {
      ao2_lock(q);
      if (!strcasecmp(args.option, "logged")) {
         mem_iter = ao2_iterator_init(q->members, 0);
         while ((m = ao2_iterator_next(&mem_iter))) {
            /* Count the agents who are logged in and presently answering calls */
            if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
               count++;
            }
            ao2_ref(m, -1);
         }
         ao2_iterator_destroy(&mem_iter);
      } else if (!strcasecmp(args.option, "free")) {
         mem_iter = ao2_iterator_init(q->members, 0);
         while ((m = ao2_iterator_next(&mem_iter))) {
            /* Count the agents who are logged in and presently answering calls */
            if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
               count++;
            }
            ao2_ref(m, -1);
         }
         ao2_iterator_destroy(&mem_iter);
      } else if (!strcasecmp(args.option, "ready")) {
         time_t now;
         time(&now);
         mem_iter = ao2_iterator_init(q->members, 0);
         while ((m = ao2_iterator_next(&mem_iter))) {
            /* Count the agents who are logged in, not paused and not wrapping up */
            if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
                  !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
               count++;
            }
            ao2_ref(m, -1);
         }
         ao2_iterator_destroy(&mem_iter);
      } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
         count = ao2_container_count(q->members);
      } else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
            ((m = interface_exists(q, args.interface)))) {
         count = m->penalty;
         ao2_ref(m, -1);
      } else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
            ((m = interface_exists(q, args.interface)))) {
         count = m->paused;
         ao2_ref(m, -1);
      } else if ( (!strcasecmp(args.option, "ignorebusy") || !strcasecmp(args.option, "ringinuse")) &&
            !ast_strlen_zero(args.interface) &&
            ((m = interface_exists(q, args.interface)))) {
         count = m->ringinuse;
         ao2_ref(m, -1);
      } else if (!ast_strlen_zero(args.interface)) {
         ast_log(LOG_ERROR, "Queue member interface %s not in queue %s\n",
            args.interface, args.queuename);
      } else {
         ast_log(LOG_ERROR, "Unknown option %s provided to %s, valid values are: "
            "logged, free, ready, count, penalty, paused, ringinuse\n", args.option, cmd);
      }
      ao2_unlock(q);
      queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
   } else {
      ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
   }

   snprintf(buf, len, "%d", count);

   return 0;
}
static int queue_function_mem_write ( struct ast_channel chan,
const char *  cmd,
char *  data,
const char *  value 
) [static]

Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ringinuse.

Definition at line 7432 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), find_load_queue_rt_friendly(), member::interface, interface_exists(), LOG_ERROR, MEMBER_PENALTY, call_queue::name, member::paused, member::realtime, member::ringinuse, set_member_value(), and update_realtime_member_field().

{
   int memvalue;
   struct call_queue *q;
   struct member *m;
   char rtvalue[80];

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(option);
      AST_APP_ARG(interface);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
      return -1;
   }

   AST_STANDARD_APP_ARGS(args, data);

   if (args.argc < 3) {
      ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
      return -1;
   }

   if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
      ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
      return -1;
   }

   memvalue = atoi(value);
   if (!strcasecmp(args.option, "penalty")) {
      /* if queuename = NULL then penalty will be set for interface in all the queues.*/
      if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, memvalue)) {
         ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
         return -1;
      }
   } else if ((q = find_load_queue_rt_friendly(args.queuename))) {
      ao2_lock(q);
      if ((m = interface_exists(q, args.interface))) {
         sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
         if (!strcasecmp(args.option, "paused")) {
            if (m->realtime) {
               update_realtime_member_field(m, q->name, args.option, rtvalue);
            } else {
               m->paused = (memvalue <= 0) ? 0 : 1;
            }
         } else if ((!strcasecmp(args.option, "ignorebusy")) || (!strcasecmp(args.option, "ringinuse"))) {
            if (m->realtime) {
               update_realtime_member_field(m, q->name, args.option, rtvalue);
            } else {
               m->ringinuse = (memvalue <= 0) ? 0 : 1;
            }
         } else {
            ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ringinuse/ignorebusy are valid\n");
            ao2_ref(m, -1);
            ao2_unlock(q);
            ao2_ref(q, -1);
            return -1;
         }
         ao2_ref(m, -1);
      } else {
         ao2_unlock(q);
         ao2_ref(q, -1);
         ast_log(LOG_ERROR, "Invalid interface for queue\n");
         return -1;
      }
      ao2_unlock(q);
      ao2_ref(q, -1);
        } else {
      ast_log(LOG_ERROR, "Invalid queue\n");
      return -1;
   }
   return 0;
}
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 7641 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.

{
   int penalty;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(interface);
   );
   /* Make sure the returned value on error is NULL. */
   buf[0] = '\0';

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
      return -1;
   }

   AST_STANDARD_APP_ARGS(args, data);

   if (args.argc < 2) {
      ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
      return -1;
   }

   penalty = get_member_penalty (args.queuename, args.interface);

   if (penalty >= 0) { /* remember that buf is already '\0' */
      snprintf (buf, len, "%d", penalty);
   }

   return 0;
}
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 7673 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, MEMBER_PENALTY, and set_member_value().

{
   int penalty;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(interface);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
      return -1;
   }

   AST_STANDARD_APP_ARGS(args, data);

   if (args.argc < 2) {
      ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
      return -1;
   }

   penalty = atoi(value);

   if (ast_strlen_zero(args.interface)) {
      ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
      return -1;
   }

   /* if queuename = NULL then penalty will be set for interface in all the queues. */
   if (set_member_value(args.queuename, args.interface, MEMBER_PENALTY, penalty)) {
      ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
      return -1;
   }

   return 0;
}
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:
numberof members
-1on error

Definition at line 7513 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(), find_load_queue_rt_friendly(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::members, queue_t_unref, and member::status.

{
   int count = 0;
   struct member *m;
   struct call_queue *q;
   struct ao2_iterator mem_iter;
   static int depflag = 1;

   if (depflag) {
      depflag = 0;
      ast_log(LOG_NOTICE, "The function QUEUE_MEMBER_COUNT has been deprecated in favor of the QUEUE_MEMBER function and will not be in further releases.\n");
   }

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
      return -1;
   }

   if ((q = find_load_queue_rt_friendly(data))) {
      ao2_lock(q);
      mem_iter = ao2_iterator_init(q->members, 0);
      while ((m = ao2_iterator_next(&mem_iter))) {
         /* Count the agents who are logged in and presently answering calls */
         if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
            count++;
         }
         ao2_ref(m, -1);
      }
      ao2_iterator_destroy(&mem_iter);
      ao2_unlock(q);
      queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
   } else {
      ast_log(LOG_WARNING, "queue %s was not found\n", data);
   }

   snprintf(buf, len, "%d", count);

   return 0;
}
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 7591 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, call_queue::name, OBJ_POINTER, queue_t_unref, and queues.

{
   struct call_queue *q, tmpq = {
      .name = data,
   };
   struct member *m;

   /* Ensure an otherwise empty list doesn't return garbage */
   buf[0] = '\0';

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
      return -1;
   }

   if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
      int buflen = 0, count = 0;
      struct ao2_iterator mem_iter;

      ao2_lock(q);
      mem_iter = ao2_iterator_init(q->members, 0);
      while ((m = ao2_iterator_next(&mem_iter))) {
         /* strcat() is always faster than printf() */
         if (count++) {
            strncat(buf + buflen, ",", len - buflen - 1);
            buflen++;
         }
         strncat(buf + buflen, m->interface, len - buflen - 1);
         buflen += strlen(m->interface);
         /* Safeguard against overflow (negative length) */
         if (buflen >= len - 2) {
            ao2_ref(m, -1);
            ast_log(LOG_WARNING, "Truncating list\n");
            break;
         }
         ao2_ref(m, -1);
      }
      ao2_iterator_destroy(&mem_iter);
      ao2_unlock(q);
      queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
   } else
      ast_log(LOG_WARNING, "queue %s was not found\n", data);

   /* We should already be terminated, but let's make sure. */
   buf[len - 1] = '\0';

   return 0;
}
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 7554 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, call_queue::name, OBJ_POINTER, queue_t_unref, queues, SENTINEL, and var.

{
   int count = 0;
   struct call_queue *q, tmpq = {
      .name = data,
   };
   struct ast_variable *var = NULL;

   buf[0] = '\0';

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
      return -1;
   }

   if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
      ao2_lock(q);
      count = q->count;
      ao2_unlock(q);
      queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
   } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
      /* if the queue is realtime but was not found in memory, this
       * means that the queue had been deleted from memory since it was
       * "dead." This means it has a 0 waiting count
       */
      count = 0;
      ast_variables_destroy(var);
   } else {
      ast_log(LOG_WARNING, "queue %s was not found\n", data);
   }

   snprintf(buf, len, "%d", count);

   return 0;
}
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:
0on success
-1on error

Definition at line 7264 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, call_queue::name, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_t_unref, queues, call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.

{
   int res = -1;
   struct call_queue *q, tmpq = {
      .name = data,
   };

   char interfacevar[256] = "";
   float sl = 0;

   if (ast_strlen_zero(data)) {
      ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
      return -1;
   }

   if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
      ao2_lock(q);
      if (q->setqueuevar) {
         sl = 0;
         res = 0;

         if (q->callscompleted > 0) {
            sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
         }

         snprintf(interfacevar, sizeof(interfacevar),
            "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
            q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);

         pbx_builtin_setvar_multiple(chan, interfacevar);
      }

      ao2_unlock(q);
      queue_t_unref(q, "Done with QUEUE() function");
   } else {
      ast_log(LOG_WARNING, "queue %s was not found\n", data);
   }

   snprintf(buf, len, "%d", res);

   return 0;
}
static int queue_hash_cb ( const void *  obj,
const int  flags 
) [static]

Definition at line 1381 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

{
   const struct call_queue *q = obj;

   return ast_str_case_hash(q->name);
}
static int queue_member_decrement_followers ( void *  obj,
void *  arg,
int  flag 
) [static]

Definition at line 1399 of file app_queue.c.

References member::queuepos.

Referenced by queue_delme_members_decrement_followers(), and queue_member_follower_removal().

{
   struct member *mem = obj;
   int *decrement_followers_after = arg;

   if (mem->queuepos > *decrement_followers_after) {
      mem->queuepos--;
   }

   return 0;
}
static void queue_member_follower_removal ( struct call_queue queue,
struct member mem 
) [static]

Definition at line 1435 of file app_queue.c.

References ao2_callback, call_queue::members, OBJ_MULTIPLE, OBJ_NODATA, queue_ent::pos, queue_member_decrement_followers(), member::queuepos, and call_queue::rrpos.

Referenced by member_remove_from_queue().

{
   int pos = mem->queuepos;

   /* If the position being removed is less than the current place in the queue, reduce the queue position by one so that we don't skip the member
    * who would have been next otherwise. */
   if (pos < queue->rrpos) {
      queue->rrpos--;
   }

   ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &pos);
}
static struct call_queue* queue_ref ( struct call_queue q) [static, read]

Definition at line 1475 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

{
   ao2_ref(q, 1);
   return q;
}
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 7801 of file app_queue.c.

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

{
   const char *general_val = NULL;
   queue_persistent_members = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
      queue_persistent_members = ast_true(general_val);
   }
   autofill_default = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
      autofill_default = ast_true(general_val);
   }
   montype_default = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
      if (!strcasecmp(general_val, "mixmonitor"))
         montype_default = 1;
   }
   update_cdr = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) {
      update_cdr = ast_true(general_val);
   }
   shared_lastcall = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
      shared_lastcall = ast_true(general_val);
   }
   negative_penalty_invalid = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
      negative_penalty_invalid = ast_true(general_val);
   }
   log_membername_as_agent = 0;
   if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
      log_membername_as_agent = ast_true(general_val);
   }
}
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 2243 of file app_queue.c.

References queue_ent::announce, call_queue::announce_to_first_user, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, autopause2int(), call_queue::autopausebusy, call_queue::autopausedelay, call_queue::autopauseunavail, queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, queue_ent::moh, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::numperiodicannounce, parse_empty_options(), call_queue::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, call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

{
   if (!strcasecmp(param, "musicclass") ||
      !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
      ast_string_field_set(q, moh, val);
   } else if (!strcasecmp(param, "announce")) {
      ast_string_field_set(q, announce, val);
   } else if (!strcasecmp(param, "context")) {
      ast_string_field_set(q, context, val);
   } else if (!strcasecmp(param, "timeout")) {
      q->timeout = atoi(val);
      if (q->timeout < 0) {
         q->timeout = DEFAULT_TIMEOUT;
      }
   } else if (!strcasecmp(param, "ringinuse")) {
      q->ringinuse = ast_true(val);
   } else if (!strcasecmp(param, "setinterfacevar")) {
      q->setinterfacevar = ast_true(val);
   } else if (!strcasecmp(param, "setqueuevar")) {
      q->setqueuevar = ast_true(val);
   } else if (!strcasecmp(param, "setqueueentryvar")) {
      q->setqueueentryvar = ast_true(val);
   } else if (!strcasecmp(param, "monitor-format")) {
      ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
   } else if (!strcasecmp(param, "membermacro")) {
      ast_string_field_set(q, membermacro, val);
   } else if (!strcasecmp(param, "membergosub")) {
      ast_string_field_set(q, membergosub, val);
   } else if (!strcasecmp(param, "queue-youarenext")) {
      ast_string_field_set(q, sound_next, val);
   } else if (!strcasecmp(param, "queue-thereare")) {
      ast_string_field_set(q, sound_thereare, val);
   } else if (!strcasecmp(param, "queue-callswaiting")) {
      ast_string_field_set(q, sound_calls, val);
   } else if (!strcasecmp(param, "queue-quantity1")) {
      ast_string_field_set(q, queue_quantity1, val);
   } else if (!strcasecmp(param, "queue-quantity2")) {
      ast_string_field_set(q, queue_quantity2, val);
   } else if (!strcasecmp(param, "queue-holdtime")) {
      ast_string_field_set(q, sound_holdtime, val);
   } else if (!strcasecmp(param, "queue-minutes")) {
      ast_string_field_set(q, sound_minutes, val);
   } else if (!strcasecmp(param, "queue-minute")) {
      ast_string_field_set(q, sound_minute, val);
   } else if (!strcasecmp(param, "queue-seconds")) {
      ast_string_field_set(q, sound_seconds, val);
   } else if (!strcasecmp(param, "queue-thankyou")) {
      ast_string_field_set(q, sound_thanks, val);
   } else if (!strcasecmp(param, "queue-callerannounce")) {
      ast_string_field_set(q, sound_callerannounce, val);
   } else if (!strcasecmp(param, "queue-reporthold")) {
      ast_string_field_set(q, sound_reporthold, val);
   } else if (!strcasecmp(param, "announce-frequency")) {
      q->announcefrequency = atoi(val);
   } else if (!strcasecmp(param, "announce-to-first-user")) {
      q->announce_to_first_user = ast_true(val);
   } else if (!strcasecmp(param, "min-announce-frequency")) {
      q->minannouncefrequency = atoi(val);
      ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
   } else if (!strcasecmp(param, "announce-round-seconds")) {
      q->roundingseconds = atoi(val);
      /* Rounding to any other values just doesn't make sense... */
      if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
         || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
         if (linenum >= 0) {
            ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
               "using 0 instead for queue '%s' at line %d of queues.conf\n",
               val, param, q->name, linenum);
         } else {
            ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
               "using 0 instead for queue '%s'\n", val, param, q->name);
         }
         q->roundingseconds=0;
      }
   } else if (!strcasecmp(param, "announce-holdtime")) {
      if (!strcasecmp(val, "once")) {
         q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
      } else if (ast_true(val)) {
         q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
      } else {
         q->announceholdtime = 0;
      }
   } else if (!strcasecmp(param, "announce-position")) {
      if (!strcasecmp(val, "limit")) {
         q->announceposition = ANNOUNCEPOSITION_LIMIT;
      } else if (!strcasecmp(val, "more")) {
         q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
      } else if (ast_true(val)) {
         q->announceposition = ANNOUNCEPOSITION_YES;
      } else {
         q->announceposition = ANNOUNCEPOSITION_NO;
      }
   } else if (!strcasecmp(param, "announce-position-limit")) {
      q->announcepositionlimit = atoi(val);
   } else if (!strcasecmp(param, "periodic-announce")) {
      if (strchr(val, ',')) {
         char *s, *buf = ast_strdupa(val);
         unsigned int i = 0;

         while ((s = strsep(&buf, ",|"))) {
            if (!q->sound_periodicannounce[i]) {
               q->sound_periodicannounce[i] = ast_str_create(16);
            }
            ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
            i++;
            if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
               break;
            }
         }
         q->numperiodicannounce = i;
      } else {
         ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
         q->numperiodicannounce = 1;
      }
   } else if (!strcasecmp(param, "periodic-announce-frequency")) {
      q->periodicannouncefrequency = atoi(val);
   } else if (!strcasecmp(param, "relative-periodic-announce")) {
      q->relativeperiodicannounce = ast_true(val);
   } else if (!strcasecmp(param, "random-periodic-announce")) {
      q->randomperiodicannounce = ast_true(val);
   } else if (!strcasecmp(param, "retry")) {
      q->retry = atoi(val);
      if (q->retry <= 0) {
         q->retry = DEFAULT_RETRY;
      }
   } else if (!strcasecmp(param, "wrapuptime")) {
      q->wrapuptime = atoi(val);
   } else if (!strcasecmp(param, "penaltymemberslimit")) {
      if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
         q->penaltymemberslimit = 0;
      }
   } else if (!strcasecmp(param, "autofill")) {
      q->autofill = ast_true(val);
   } else if (!strcasecmp(param, "monitor-type")) {
      if (!strcasecmp(val, "mixmonitor")) {
         q->montype = 1;
      }
   } else if (!strcasecmp(param, "autopause")) {
      q->autopause = autopause2int(val);
   } else if (!strcasecmp(param, "autopausedelay")) {
      q->autopausedelay = atoi(val);
   } else if (!strcasecmp(param, "autopausebusy")) {
      q->autopausebusy = ast_true(val);
   } else if (!strcasecmp(param, "autopauseunavail")) {
      q->autopauseunavail = ast_true(val);
   } else if (!strcasecmp(param, "maxlen")) {
      q->maxlen = atoi(val);
      if (q->maxlen < 0) {
         q->maxlen = 0;
      }
   } else if (!strcasecmp(param, "servicelevel")) {
      q->servicelevel= atoi(val);
   } else if (!strcasecmp(param, "strategy")) {
      int strategy;

      /* We are a static queue and already have set this, no need to do it again */
      if (failunknown) {
         return;
      }
      strategy = strat2int(val);
      if (strategy < 0) {
         ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
            val, q->name);
         q->strategy = QUEUE_STRATEGY_RINGALL;
      }
      if (strategy == q->strategy) {
         return;
      }
      if (strategy == QUEUE_STRATEGY_LINEAR) {
         ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
         return;
      }
      q->strategy = strategy;
   } else if (!strcasecmp(param, "joinempty")) {
      parse_empty_options(val, &q->joinempty, 1);
   } else if (!strcasecmp(param, "leavewhenempty")) {
      parse_empty_options(val, &q->leavewhenempty, 0);
   } else if (!strcasecmp(param, "eventmemberstatus")) {
      q->maskmemberstatus = !ast_true(val);
   } else if (!strcasecmp(param, "eventwhencalled")) {
      if (!strcasecmp(val, "vars")) {
         q->eventwhencalled = QUEUE_EVENT_VARIABLES;
      } else {
         q->eventwhencalled = ast_true(val) ? 1 : 0;
      }
   } else if (!strcasecmp(param, "reportholdtime")) {
      q->reportholdtime = ast_true(val);
   } else if (!strcasecmp(param, "memberdelay")) {
      q->memberdelay = atoi(val);
   } else if (!strcasecmp(param, "weight")) {
      q->weight = atoi(val);
   } else if (!strcasecmp(param, "timeoutrestart")) {
      q->timeoutrestart = ast_true(val);
   } else if (!strcasecmp(param, "defaultrule")) {
      ast_string_field_set(q, defaultrule, val);
   } else if (!strcasecmp(param, "timeoutpriority")) {
      if (!strcasecmp(val, "conf")) {
         q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
      } else {
         q->timeoutpriority = TIMEOUT_PRIORITY_APP;
      }
   } else if (failunknown) {
      if (linenum >= 0) {
         ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
            q->name, param, linenum);
      } else {
         ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
      }
   }
}
static char* queue_show ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

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

{
   switch ( cmd ) {
   case CLI_INIT:
      e->command = "queue show";
      e->usage =
         "Usage: queue show\n"
         "       Provides summary information on a specified queue.\n";
      return NULL;
   case CLI_GENERATE:
      return complete_queue_show(a->line, a->word, a->pos, a->n);
   }

   return __queues_show(NULL, a->fd, a->argc, a->argv);
}
static void queue_transfer_destroy ( void *  data) [static]

Definition at line 5023 of file app_queue.c.

References ast_free.

{
   struct queue_transfer_ds *qtds = data;
   ast_free(qtds);
}
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 5046 of file app_queue.c.

References ast_channel_context(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_exten(), ast_channel_uniqueid(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, 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, and update_queue().

{
   struct queue_transfer_ds *qtds = data;
   struct queue_ent *qe = qtds->qe;
   struct member *member = qtds->member;
   time_t callstart = qtds->starttime;
   int callcompletedinsl = qtds->callcompletedinsl;
   struct ast_datastore *datastore;

   ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
            ast_channel_exten(new_chan), ast_channel_context(new_chan), (long) (callstart - qe->start),
            (long) (time(NULL) - callstart), qe->opos);

   update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));

   /* No need to lock the channels because they are already locked in ast_do_masquerade */
   if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
      ast_channel_datastore_remove(old_chan, datastore);
   } else {
      ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
   }
}
static struct call_queue* queue_unref ( struct call_queue q) [static, read]

Definition at line 1481 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec(), and queues_data_provider_get().

{
   ao2_ref(q, -1);
   return NULL;
}
static int queues_data_provider_get ( const struct ast_data_search search,
struct ast_data data_root 
) [static]

Definition at line 9821 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(), find_load_queue_rt_friendly(), call_queue::name, queue_unref(), queues, queues_data_provider_get_helper(), call_queue::realtime, and SENTINEL.

{
   struct ao2_iterator i;
   struct call_queue *queue, *queue_realtime = NULL;
   struct ast_config *cfg;
   char *queuename;

   /* load realtime queues. */
   cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
   if (cfg) {
      for (queuename = ast_category_browse(cfg, NULL);
            !ast_strlen_zero(queuename);
            queuename = ast_category_browse(cfg, queuename)) {
         if ((queue = find_load_queue_rt_friendly(queuename))) {
            queue_unref(queue);
         }
      }
      ast_config_destroy(cfg);
   }

   /* static queues. */
   i = ao2_iterator_init(queues, 0);
   while ((queue = ao2_iterator_next(&i))) {
      ao2_lock(queue);
      if (queue->realtime) {
         queue_realtime = find_load_queue_rt_friendly(queue->name);
         if (!queue_realtime) {
            ao2_unlock(queue);
            queue_unref(queue);
            continue;
         }
         queue_unref(queue_realtime);
      }

      queues_data_provider_get_helper(search, data_root, queue);
      ao2_unlock(queue);
      queue_unref(queue);
   }
   ao2_iterator_destroy(&i);

   return 0;
}
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 9715 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().

{
   struct ao2_iterator im;
   struct member *member;
   struct queue_ent *qe;
   struct ast_data *data_queue, *data_members = NULL, *enum_node;
   struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;

   data_queue = ast_data_add_node(data_root, "queue");
   if (!data_queue) {
      return;
   }

   ast_data_add_structure(call_queue, data_queue, queue);

   ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
   ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));

   /* announce position */
   enum_node = ast_data_add_node(data_queue, "announceposition");
   if (!enum_node) {
      return;
   }
   switch (queue->announceposition) {
   case ANNOUNCEPOSITION_LIMIT:
      ast_data_add_str(enum_node, "text", "limit");
      break;
   case ANNOUNCEPOSITION_MORE_THAN:
      ast_data_add_str(enum_node, "text", "more");
      break;
   case ANNOUNCEPOSITION_YES:
      ast_data_add_str(enum_node, "text", "yes");
      break;
   case ANNOUNCEPOSITION_NO:
      ast_data_add_str(enum_node, "text", "no");
      break;
   default:
      ast_data_add_str(enum_node, "text", "unknown");
      break;
   }
   ast_data_add_int(enum_node, "value", queue->announceposition);

   /* add queue members */
   im = ao2_iterator_init(queue->members, 0);
   while ((member = ao2_iterator_next(&im))) {
      if (!data_members) {
         data_members = ast_data_add_node(data_queue, "members");
         if (!data_members) {
            ao2_ref(member, -1);
            continue;
         }
      }

      data_member = ast_data_add_node(data_members, "member");
      if (!data_member) {
         ao2_ref(member, -1);
         continue;
      }

      ast_data_add_structure(member, data_member, member);

      ao2_ref(member, -1);
   }
   ao2_iterator_destroy(&im);

   /* include the callers inside the result. */
   if (queue->head) {
      for (qe = queue->head; qe; qe = qe->next) {
         if (!data_callers) {
            data_callers = ast_data_add_node(data_queue, "callers");
            if (!data_callers) {
               continue;
            }
         }

         data_caller = ast_data_add_node(data_callers, "caller");
         if (!data_caller) {
            continue;
         }

         ast_data_add_structure(queue_ent, data_caller, qe);

         /* add the caller channel. */
         data_caller_channel = ast_data_add_node(data_caller, "channel");
         if (!data_caller_channel) {
            continue;
         }

         ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
      }
   }

   /* if this queue doesn't match remove the added queue. */
   if (!ast_data_search_match(search, data_queue)) {
      ast_data_remove_node(data_root, data_queue);
   }
}
static void recalc_holdtime ( struct queue_ent qe,
int  newholdtime 
) [static]

Definition at line 3238 of file app_queue.c.

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

Referenced by try_calling().

{
   int oldvalue;

   /* Calculate holdtime using an exponential average */
   /* Thanks to SRT for this contribution */
   /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */

   ao2_lock(qe->parent);
   oldvalue = qe->parent->holdtime;
   qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
   ao2_unlock(qe->parent);
}
static void record_abandoned ( struct queue_ent qe) [static]

Record that a caller gave up on waiting in queue.

Definition at line 3970 of file app_queue.c.

References ao2_lock, ao2_unlock, ast_channel_uniqueid(), 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(), and queue_ent::start.

Referenced by queue_exec(), and try_calling().

{
   set_queue_variables(qe->parent, qe->chan);
   ao2_lock(qe->parent);
   /*** DOCUMENTATION
   <managerEventInstance>
      <synopsis>Raised when an caller abandons the queue.</synopsis>
      <syntax>
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='Join']/managerEventInstance/syntax/parameter[@name='Position'])" />
         <parameter name="OriginalPosition">
            <para>The channel's original position in the queue.</para>
         </parameter>
         <parameter name="HoldTime">
            <para>The time the channel was in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
         </parameter>
      </syntax>
   </managerEventInstance>
   ***/
   manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
      "Queue: %s\r\n"
      "Uniqueid: %s\r\n"
      "Position: %d\r\n"
      "OriginalPosition: %d\r\n"
      "HoldTime: %d\r\n",
      qe->parent->name, ast_channel_uniqueid(qe->chan), qe->pos, qe->opos, (int)(time(NULL) - qe->start));

   qe->parent->callsabandoned++;
   ao2_unlock(qe->parent);
}
static int reload ( void  ) [static]

Definition at line 10010 of file app_queue.c.

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

{
   struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
   ast_unload_realtime("queue_members");
   reload_handler(1, &mask, NULL);
   return 0;
}
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:
reloadTrue if we are reloading information, false if we are loading information for the first time.
maskA bitmask which tells the handler what actions to take
queuenameThe name of the queue on which we wish to take action
Return values:
0All reloads were successful
non-zeroThere was a failure

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

{
   int res = 0;

   if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
      res |= reload_queue_rules(reload);
   }
   if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
      res |= clear_stats(queuename);
   }
   if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
      res |= reload_queues(reload, mask, queuename);
   }
   return res;
}
static void reload_queue_members ( void  ) [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 6558 of file app_queue.c.

References add_to_queue(), ao2_t_find, ast_db_del(), ast_db_freetree(), ast_db_get_allocated(), ast_db_gettree(), ast_debug, ast_free, ast_log(), ast_strlen_zero(), errno, find_load_queue_rt_friendly(), member::interface, ast_db_entry::key, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, queue_t_unref, queues, RES_OUTOFMEMORY, and member::state_interface.

Referenced by load_module().

{
   char *cur_ptr;
   const char *queue_name;
   char *member;
   char *interface;
   char *membername = NULL;
   char *state_interface;
   char *penalty_tok;
   int penalty = 0;
   char *paused_tok;
   int paused = 0;
   struct ast_db_entry *db_tree;
   struct ast_db_entry *entry;
   struct call_queue *cur_queue;
   char *queue_data;

   /* Each key in 'pm_family' is the name of a queue */
   db_tree = ast_db_gettree(pm_family, NULL);
   for (entry = db_tree; entry; entry = entry->next) {

      queue_name = entry->key + strlen(pm_family) + 2;

      {
         struct call_queue tmpq = {
            .name = queue_name,
         };
         cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
      }

      if (!cur_queue) {
         cur_queue = find_load_queue_rt_friendly(queue_name);
      }

      if (!cur_queue) {
         /* If the queue no longer exists, remove it from the
          * database */
         ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
         ast_db_del(pm_family, queue_name);
         continue;
      }

      if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
         queue_t_unref(cur_queue, "Expire reload reference");
         continue;
      }

      cur_ptr = queue_data;
      while ((member = strsep(&cur_ptr, ",|"))) {
         if (ast_strlen_zero(member)) {
            continue;
         }

         interface = strsep(&member, ";");
         penalty_tok = strsep(&member, ";");
         paused_tok = strsep(&member, ";");
         membername = strsep(&member, ";");
         state_interface = strsep(&member, ";");

         if (!penalty_tok) {
            ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
            break;
         }
         penalty = strtol(penalty_tok, NULL, 10);
         if (errno == ERANGE) {
            ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
            break;
         }

         if (!paused_tok) {
            ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
            break;
         }
         paused = strtol(paused_tok, NULL, 10);
         if ((errno == ERANGE) || paused < 0 || paused > 1) {
            ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
            break;
         }

         ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);

         if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
            ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
            break;
         }
      }
      queue_t_unref(cur_queue, "Expire reload reference");
      ast_free(queue_data);
   }

   if (db_tree) {
      ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
      ast_db_freetree(db_tree);
   }
}
static int reload_queue_rules ( int  reload) [static]

Reload the rules defined in queuerules.conf.

Parameters:
reloadIf 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 7752 of file app_queue.c.

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

Referenced by reload_handler().

{
   struct ast_config *cfg;
   struct rule_list *rl_iter, *new_rl;
   struct penalty_rule *pr_iter;
   char *rulecat = NULL;
   struct ast_variable *rulevar = NULL;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };

   if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
      ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
      return AST_MODULE_LOAD_SUCCESS;
   } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
      return AST_MODULE_LOAD_SUCCESS;
   } else if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
      return AST_MODULE_LOAD_SUCCESS;
   }

   AST_LIST_LOCK(&rule_lists);
   while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
      while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
         ast_free(pr_iter);
      ast_free(rl_iter);
   }
   while ((rulecat = ast_category_browse(cfg, rulecat))) {
      if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
         AST_LIST_UNLOCK(&rule_lists);
         ast_config_destroy(cfg);
         return AST_MODULE_LOAD_FAILURE;
      } else {
         ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
         AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
         for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
            if(!strcasecmp(rulevar->name, "penaltychange"))
               insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
            else
               ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
      }
   }
   AST_LIST_UNLOCK(&rule_lists);

   ast_config_destroy(cfg);

   return AST_MODULE_LOAD_SUCCESS;
}
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:
reload0 if we are calling this the first time, 1 every other time
maskGives flags telling us what information to actually reload
queuenameIf set to a non-zero string, then only reload information from that particular queue. Otherwise inspect all queues
Return values:
-1Failure occurred
0All clear!

Definition at line 8119 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_NOLOCK, OBJ_UNLINK, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_global_params(), queues, reload_single_queue(), and remove_members_and_mark_unfound().

Referenced by reload_handler().

{
   struct ast_config *cfg;
   char *cat;
   struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
   const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
   const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);

   if (!(cfg = ast_config_load("queues.conf", config_flags))) {
      ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
      return -1;
   } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
      return 0;
   } else if (cfg == CONFIG_STATUS_FILEINVALID) {
      ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
      return -1;
   }

   /* We've made it here, so it looks like we're doing operations on all queues. */
   ao2_lock(queues);

   /* Mark all queues as dead for the moment if we're reloading queues.
    * For clarity, we could just be reloading members, in which case we don't want to mess
    * with the other queue parameters at all*/
   if (queue_reload) {
      ao2_callback(queues, OBJ_NODATA | OBJ_NOLOCK, mark_dead_and_unfound, (char *) queuename);
   }

   if (member_reload) {
      ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename);
   }

   /* Chug through config file */
   cat = NULL;
   while ((cat = ast_category_browse(cfg, cat)) ) {
      if (!strcasecmp(cat, "general") && queue_reload) {
         queue_set_global_params(cfg);
         continue;
      }
      if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
         reload_single_queue(cfg, mask, cat);
   }

   ast_config_destroy(cfg);
   /* Unref all the dead queues if we were reloading queues */
   if (queue_reload) {
      ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NOLOCK, kill_dead_queues, (char *) queuename);
   }
   ao2_unlock(queues);
   return 0;
}
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:
memberdataThe part after member = in the config file
qThe queue to which this member belongs

Definition at line 7843 of file app_queue.c.

References ao2_find, ao2_link, ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_false(), ast_log(), AST_STANDARD_APP_ARGS, ast_strip(), ast_strlen_zero(), ast_true(), create_queue_member(), member::interface, LOG_ERROR, LOG_WARNING, member_add_to_queue(), call_queue::members, call_queue::name, OBJ_POINTER, parse(), member::paused, member::penalty, member::queuepos, member::ringinuse, and call_queue::ringinuse.

Referenced by reload_single_queue().

{
   char *membername, *interface, *state_interface, *tmp;
   char *parse;
   struct member *cur, *newm;
   struct member tmpmem;
   int penalty;
   int ringinuse;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(interface);
      AST_APP_ARG(penalty);
      AST_APP_ARG(membername);
      AST_APP_ARG(state_interface);
      AST_APP_ARG(ringinuse);
   );

   if (ast_strlen_zero(memberdata)) {
      ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
      return;
   }

   /* Add a new member */
   parse = ast_strdupa(memberdata);

   AST_STANDARD_APP_ARGS(args, parse);

   interface = args.interface;
   if (!ast_strlen_zero(args.penalty)) {
      tmp = args.penalty;
      ast_strip(tmp);
      penalty = atoi(tmp);
      if (penalty < 0) {
         penalty = 0;
      }
   } else {
      penalty = 0;
   }

   if (!ast_strlen_zero(args.membername)) {
      membername = args.membername;
      ast_strip(membername);
   } else {
      membername = interface;
   }

   if (!ast_strlen_zero(args.state_interface)) {
      state_interface = args.state_interface;
      ast_strip(state_interface);
   } else {
      state_interface = interface;
   }

   if (!ast_strlen_zero(args.ringinuse)) {
      tmp = args.ringinuse;
      ast_strip(tmp);
      if (ast_true(tmp)) {
         ringinuse = 1;
      } else if (ast_false(tmp)) {
         ringinuse = 0;
      } else {
         ast_log(LOG_ERROR, "Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
            membername, q->name);
         ringinuse = q->ringinuse;
      }
   } else {
      ringinuse = q->ringinuse;
   }

   /* Find the old position in the list */
   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);

   if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface, ringinuse))) {
      if (cur) {
         /* Round Robin Queue Position must be copied if this is replacing an existing member */
         ao2_lock(q->members);
         newm->queuepos = cur->queuepos;
         ao2_link(q->members, newm);
         ao2_unlink(q->members, cur);
         ao2_unlock(q->members);
      } else {
         /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
         member_add_to_queue(q, newm);
      }
      ao2_ref(newm, -1);
   }
   newm = NULL;

   if (cur) {
      ao2_ref(cur, -1);
   }
}
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:
cfgThe configuration which we are reading
maskTells us what information we need to reload
queuenameThe name of the queue we are reloading information from
Return values:
void

Definition at line 7968 of file app_queue.c.

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

Referenced by reload_queues().

{
   int new;
   struct call_queue *q = NULL;
   /*We're defining a queue*/
   struct call_queue tmpq = {
      .name = queuename,
   };
   const char *tmpvar;
   const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
   const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
   int prev_weight = 0;
   struct ast_variable *var;
   if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
      if (queue_reload) {
         /* Make one then */
         if (!(q = alloc_queue(queuename))) {
            return;
         }
      } else {
         /* Since we're not reloading queues, this means that we found a queue
          * in the configuration file which we don't know about yet. Just return.
          */
         return;
      }
      new = 1;
   } else {
      new = 0;
   }

   if (!new) {
      ao2_lock(q);
      prev_weight = q->weight ? 1 : 0;
   }
   /* Check if we already found a queue with this name in the config file */
   if (q->found) {
      ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
      if (!new) {
         /* It should be impossible to *not* hit this case*/
         ao2_unlock(q);
      }
      queue_t_unref(q, "We exist! Expiring temporary pointer");
      return;
   }
   /* Due to the fact that the "linear" strategy will have a different allocation
    * scheme for queue members, we must devise the queue's strategy before other initializations.
    * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
    * container used will have only a single bucket instead of the typical number.
    */
   if (queue_reload) {
      if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
         q->strategy = strat2int(tmpvar);
         if (q->strategy < 0) {
            ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
            tmpvar, q->name);
            q->strategy = QUEUE_STRATEGY_RINGALL;
         }
      } else {
         q->strategy = QUEUE_STRATEGY_RINGALL;
      }
      init_queue(q);
   }
   if (member_reload) {
      ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
      q->found = 1;
   }

   /* On the first pass we just read the parameters of the queue */
   for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
      if (queue_reload && strcasecmp(var->name, "member")) {
         queue_set_param(q, var->name, var->value, var->lineno, 1);
      }
   }

   /* On the second pass, we read members */
   for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
      if (member_reload && !strcasecmp(var->name, "member")) {
         reload_single_member(var->value, q);
      }
   }

   /* At this point, we've determined if the queue has a weight, so update use_weight
    * as appropriate
    */
   if (!q->weight && prev_weight) {
      ast_atomic_fetchadd_int(&use_weight, -1);
   } else if (q->weight && !prev_weight) {
      ast_atomic_fetchadd_int(&use_weight, +1);
   }

   /* Free remaining members marked as delme */
   if (member_reload) {
      ao2_lock(q->members);
      ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q);
      ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
      ao2_unlock(q->members);
   }

   if (new) {
      queues_t_link(queues, q, "Add queue to container");
   } else {
      ao2_unlock(q);
   }
   queue_t_unref(q, "Expiring creation reference");
}
static int remove_from_queue ( const char *  queuename,
const char *  interface 
) [static]

Remove member from queue.

Return values:
RES_NOT_DYNAMICwhen they aren't a RT member
RES_NOSUCHQUEUEqueue does not exist
RES_OKAYremoved member from queue
RES_EXISTSqueue exists but no members

Definition at line 6080 of file app_queue.c.

References ao2_find, ao2_lock, ao2_ref, ao2_t_find, ao2_unlock, ast_copy_string(), AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_strlen_zero(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event, member_remove_from_queue(), member::membername, call_queue::members, call_queue::name, num_available_members(), OBJ_POINTER, queue_t_unref, queues, member::realtime, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, member::rt_uniqueid, and update_realtime_member_field().

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

{
   struct call_queue *q, tmpq = {
      .name = queuename,
   };
   struct member *mem, tmpmem;
   int res = RES_NOSUCHQUEUE;

   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
   if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
      ao2_lock(q);
      if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
         /* XXX future changes should beware of this assumption!! */
         /*Change Penalty on realtime users*/
         if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
            update_realtime_member_field(mem, q->name, "penalty", "-1");
         } else if (!mem->dynamic) {
            ao2_ref(mem, -1);
            ao2_unlock(q);
            queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
            return RES_NOT_DYNAMIC;
         }
         /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when a member is removed from the queue.</synopsis>
            <syntax>
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
            </syntax>
            <see-also>
               <ref type="managerEvent">QueueMemberAdded</ref>
               <ref type="application">RemoveQueueMember</ref>
            </see-also>
         </managerEventInstance>
         ***/
         manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
            "Queue: %s\r\n"
            "Location: %s\r\n"
            "MemberName: %s\r\n",
            q->name, mem->interface, mem->membername);
         member_remove_from_queue(q, mem);
         ao2_ref(mem, -1);

         if (queue_persistent_members) {
            dump_queue_members(q);
         }

         if (!num_available_members(q)) {
            ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
         }

         res = RES_OKAY;
      } else {
         res = RES_EXISTS;
      }
      ao2_unlock(q);
      queue_t_unref(q, "Expiring temporary reference");
   }

   return res;
}
static int remove_members_and_mark_unfound ( void *  obj,
void *  arg,
int  flags 
) [static]

Definition at line 8074 of file app_queue.c.

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

Referenced by reload_queues().

{
   struct call_queue *q = obj;
   char *queuename = arg;
   if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
      q->found = 0;

   }
   return 0;
}
static int ring_entry ( struct queue_ent qe,
struct callattempt tmp,
int *  busies 
) [static]

Part 2 of ring_one.

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

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

Definition at line 3617 of file app_queue.c.

References ast_cdr::accountcode, ast_cdr::amaflags, ast_party_caller::ani, ast_party_connected_line::ani, ao2_lock, ao2_unlock, ast_assert, ast_call(), AST_CAUSE_ANSWERED_ELSEWHERE, ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_adsicpe_set(), ast_channel_appl_set(), ast_channel_caller(), ast_channel_cdr(), ast_channel_connected(), ast_channel_context(), ast_channel_data_set(), ast_channel_datastore_inherit(), ast_channel_dialed(), ast_channel_exten(), ast_channel_exten_set(), ast_channel_hangupcause_set(), ast_channel_inherit_variables(), ast_channel_lock_both, ast_channel_macroexten(), ast_channel_name(), ast_channel_nativeformats(), ast_channel_priority(), ast_channel_redirecting(), ast_channel_set_caller_event(), ast_channel_uniqueid(), ast_channel_unlock, ast_channel_whentohangup(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_strlen_zero(), ast_verb, member::call_pending, can_ring_entry(), queue_ent::cancel_answered_elsewhere, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_cdr::clid, ast_cdr::dcontext, callattempt::dial_callerid_absent, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_party_caller::id, ast_party_connected_line::id, callattempt::interface, ast_cdr::lastapp, ast_cdr::lastdata, queue_ent::linpos, manager_event, callattempt::member, member_call_pending_clear(), member::membername, ast_party_id::name, call_queue::name, ast_party_id::number, ast_party_dialed::number, queue_ent::parent, pbx_builtin_getvar_helper(), QUEUE_EVENT_VARIABLES, member::ringinuse, call_queue::rrpos, S_COR, S_OR, ast_cdr::src, status, callattempt::stillgoing, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_party_dialed::transit_network_select, ast_cdr::userfield, ast_party_name::valid, ast_party_number::valid, and vars2manager().

Referenced by ring_one().

{
   int res;
   int status;
   char tech[256];
   char *location;
   const char *macrocontext, *macroexten;

   /* on entry here, we know that tmp->chan == NULL */
   if (!can_ring_entry(qe, tmp)) {
      if (ast_channel_cdr(qe->chan)) {
         ast_cdr_busy(ast_channel_cdr(qe->chan));
      }
      tmp->stillgoing = 0;
      ++*busies;
      return 0;
   }
   ast_assert(tmp->member->ringinuse || tmp->member->call_pending);

   ast_copy_string(tech, tmp->interface, sizeof(tech));
   if ((location = strchr(tech, '/'))) {
      *location++ = '\0';
   } else {
      location = "";
   }

   /* Request the peer */
   tmp->chan = ast_request(tech, ast_channel_nativeformats(qe->chan), qe->chan, location, &status);
   if (!tmp->chan) {       /* If we can't, just go on to the next call */
      ao2_lock(qe->parent);
      qe->parent->rrpos++;
      qe->linpos++;
      ao2_unlock(qe->parent);

      member_call_pending_clear(tmp->member);

      if (ast_channel_cdr(qe->chan)) {
         ast_cdr_busy(ast_channel_cdr(qe->chan));
      }
      tmp->stillgoing = 0;
      ++*busies;
      return 0;
   }

   ast_channel_lock_both(tmp->chan, qe->chan);

   if (qe->cancel_answered_elsewhere) {
      ast_channel_hangupcause_set(tmp->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
   }
   ast_channel_appl_set(tmp->chan, "AppQueue");
   ast_channel_data_set(tmp->chan, "(Outgoing Line)");
   memset(ast_channel_whentohangup(tmp->chan), 0, sizeof(*ast_channel_whentohangup(tmp->chan)));

   /* If the new channel has no callerid, try to guess what it should be */
   if (!ast_channel_caller(tmp->chan)->id.number.valid) {
      if (ast_channel_connected(qe->chan)->id.number.valid) {
         struct ast_party_caller caller;

         ast_party_caller_set_init(&caller, ast_channel_caller(tmp->chan));
         caller.id = ast_channel_connected(qe->chan)->id;
         caller.ani = ast_channel_connected(qe->chan)->ani;
         ast_channel_set_caller_event(tmp->chan, &caller, NULL);
      } else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
         ast_set_callerid(tmp->chan, ast_channel_dialed(qe->chan)->number.str, NULL, NULL);
      } else if (!ast_strlen_zero(S_OR(ast_channel_macroexten(qe->chan), ast_channel_exten(qe->chan)))) {
         ast_set_callerid(tmp->chan, S_OR(ast_channel_macroexten(qe->chan), ast_channel_exten(qe->chan)), NULL, NULL);
      }
      tmp->dial_callerid_absent = 1;
   }

   ast_party_redirecting_copy(ast_channel_redirecting(tmp->chan), ast_channel_redirecting(qe->chan));

   ast_channel_dialed(tmp->chan)->transit_network_select = ast_channel_dialed(qe->chan)->transit_network_select;

   ast_connected_line_copy_from_caller(ast_channel_connected(tmp->chan), ast_channel_caller(qe->chan));

   /* Inherit specially named variables from parent channel */
   ast_channel_inherit_variables(qe->chan, tmp->chan);
   ast_channel_datastore_inherit(qe->chan, tmp->chan);

   /* Presense of ADSI CPE on outgoing channel follows ours */
   ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->chan));

   /* Inherit context and extension */
   macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
   ast_channel_dialcontext_set(tmp->chan, ast_strlen_zero(macrocontext) ? ast_channel_context(qe->chan) : macrocontext);
   macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
   if (!ast_strlen_zero(macroexten)) {
      ast_channel_exten_set(tmp->chan, macroexten);
   } else {
      ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
   }
   if (ast_cdr_isset_unanswered()) {
      /* they want to see the unanswered dial attempts! */
      /* set up the CDR fields on all the CDRs to give sensical information */
      ast_cdr_setdestchan(ast_channel_cdr(tmp->chan), ast_channel_name(tmp->chan));
      strcpy(ast_channel_cdr(tmp->chan)->clid, ast_channel_cdr(qe->chan)->clid);
      strcpy(ast_channel_cdr(tmp->chan)->channel, ast_channel_cdr(qe->chan)->channel);
      strcpy(ast_channel_cdr(tmp->chan)->src, ast_channel_cdr(qe->chan)->src);
      strcpy(ast_channel_cdr(tmp->chan)->dst, ast_channel_exten(qe->chan));
      strcpy(ast_channel_cdr(tmp->chan)->dcontext, ast_channel_context(qe->chan));
      strcpy(ast_channel_cdr(tmp->chan)->lastapp, ast_channel_cdr(qe->chan)->lastapp);
      strcpy(ast_channel_cdr(tmp->chan)->lastdata, ast_channel_cdr(qe->chan)->lastdata);
      ast_channel_cdr(tmp->chan)->amaflags = ast_channel_cdr(qe->chan)->amaflags;
      strcpy(ast_channel_cdr(tmp->chan)->accountcode, ast_channel_cdr(qe->chan)->accountcode);
      strcpy(ast_channel_cdr(tmp->chan)->userfield, ast_channel_cdr(qe->chan)->userfield);
   }

   ast_channel_unlock(tmp->chan);
   ast_channel_unlock(qe->chan);

   /* Place the call, but don't wait on the answer */
   if ((res = ast_call(tmp->chan, location, 0))) {
      /* Again, keep going even if there's an error */
      ast_verb(3, "Couldn't call %s\n", tmp->interface);
      do_hang(tmp);
      member_call_pending_clear(tmp->member);
      ++*busies;
      return 0;
   }

   if (qe->parent->eventwhencalled) {
      char vars[2048];

      ast_channel_lock_both(tmp->chan, qe->chan);

      /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when an Agent is notified of a member in the queue.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
            <parameter name="AgentCalled">
               <para>The agent's technology or location.</para>
            </parameter>
            <parameter name="AgentName">
               <para>The name of the agent.</para>
            </parameter>
            <parameter name="Variable" required="no" multiple="yes">
               <para>Optional channel variables from the ChannelCalling channel</para>
            </parameter>
         </syntax>
         <see-also>
            <ref type="managerEvent">AgentRingNoAnswer</ref>
            <ref type="managerEvent">AgentComplete</ref>
            <ref type="managerEvent">AgentConnect</ref>
         </see-also>
      </managerEventInstance>
      ***/
      manager_event(EVENT_FLAG_AGENT, "AgentCalled",
         "Queue: %s\r\n"
         "AgentCalled: %s\r\n"
         "AgentName: %s\r\n"
         "ChannelCalling: %s\r\n"
         "DestinationChannel: %s\r\n"
         "CallerIDNum: %s\r\n"
         "CallerIDName: %s\r\n"
         "ConnectedLineNum: %s\r\n"
         "ConnectedLineName: %s\r\n"
         "Context: %s\r\n"
         "Extension: %s\r\n"
         "Priority: %d\r\n"
         "Uniqueid: %s\r\n"
         "%s",
         qe->parent->name, tmp->interface, tmp->member->membername, ast_channel_name(qe->chan), ast_channel_name(tmp->chan),
         S_COR(ast_channel_caller(qe->chan)->id.number.valid, ast_channel_caller(qe->chan)->id.number.str, "unknown"),
         S_COR(ast_channel_caller(qe->chan)->id.name.valid, ast_channel_caller(qe->chan)->id.name.str, "unknown"),
         S_COR(ast_channel_connected(qe->chan)->id.number.valid, ast_channel_connected(qe->chan)->id.number.str, "unknown"),
         S_COR(ast_channel_connected(qe->chan)->id.name.valid, ast_channel_connected(qe->chan)->id.name.str, "unknown"),
         ast_channel_context(qe->chan), ast_channel_exten(qe->chan), ast_channel_priority(qe->chan), ast_channel_uniqueid(qe->chan),
         qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");

      ast_channel_unlock(tmp->chan);
      ast_channel_unlock(qe->chan);

      ast_verb(3, "Called %s\n", tmp->interface);
   }

   member_call_pending_clear(tmp->member);
   return 1;
}
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:
1if a member was called successfully
0otherwise

Definition at line 3824 of file app_queue.c.

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

Referenced by try_calling(), and wait_for_answer().

{
   int ret = 0;

   while (ret == 0) {
      struct callattempt *best = find_best(outgoing);
      if (!best) {
         ast_debug(1, "Nobody left to try ringing in queue\n");
         break;
      }
      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
         struct callattempt *cur;
         /* Ring everyone who shares this best metric (for ringall) */
         for (cur = outgoing; cur; cur = cur->q_next) {
            if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
               ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
               ret |= ring_entry(qe, cur, busies);
            }
         }
      } else {
         /* Ring just the best channel */
         ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
         ret = ring_entry(qe, best, busies);
      }

      /* If we have timed out, break out */
      if (qe->expire && (time(NULL) >= qe->expire)) {
         ast_debug(1, "Queue timed out while ringing members.\n");
         ret = 0;
         break;
      }
   }

   return ret;
}
static void rna ( int  rnatime,
struct queue_ent qe,
char *  interface,
char *  membername,
int  autopause 
) [static]

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

Definition at line 4002 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, ast_channel_name(), ast_channel_uniqueid(), ast_indicate(), ast_moh_start(), ast_queue_log(), ast_verb, call_queue::autopause, call_queue::autopausedelay, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, interface_exists(), member::lastcall, manager_event, queue_ent::moh, call_queue::name, queue_ent::parent, QUEUE_AUTOPAUSE_OFF, QUEUE_AUTOPAUSE_ON, QUEUE_EVENT_VARIABLES, queue_ent::ring_when_ringing, set_member_paused(), and vars2manager().

Referenced by wait_for_answer().

{
   ast_verb(3, "Nobody picked up in %d ms\n", rnatime);

   /* Stop ringing, and resume MOH if specified */
   if (qe->ring_when_ringing) {
      ast_indicate(qe->chan, -1);
      ast_moh_start(qe->chan, qe->moh, NULL);
   }

   if (qe->parent->eventwhencalled) {
      char vars[2048];
      /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when an agent is notified of a member in the queue and fails to answer.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
            <parameter name="Member">
               <para>The queue member's channel technology or location.</para>
            </parameter>
            <parameter name="RingTime">
               <para>The time the agent was rung, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
            </parameter>
         </syntax>
         <see-also>
            <ref type="managerEvent">AgentCalled</ref>
         </see-also>
      </managerEventInstance>
      ***/
      manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
                  "Queue: %s\r\n"
                  "Uniqueid: %s\r\n"
                  "Channel: %s\r\n"
                  "Member: %s\r\n"
                  "MemberName: %s\r\n"
                  "RingTime: %d\r\n"
                  "%s",
                  qe->parent->name,
                  ast_channel_uniqueid(qe->chan),
                  ast_channel_name(qe->chan),
                  interface,
                  membername,
                  rnatime,
                  qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
   }
   ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
   if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && autopause) {
      if (qe->parent->autopausedelay > 0) {
         struct member *mem;
         ao2_lock(qe->parent);
         if ((mem = interface_exists(qe->parent, interface))) {
            time_t idletime = time(&idletime)-mem->lastcall;
            if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
               ao2_unlock(qe->parent);
               ao2_ref(mem, -1);
               return;
            }
            ao2_ref(mem, -1);
         }
         ao2_unlock(qe->parent);
      }
      if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
         if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
            ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
               interface, qe->parent->name);
         } else {
            ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
         }
      } else {
         /* If queue autopause is mode all, just don't send any queue to stop.
         * the function will stop in all queues */
         if (!set_member_paused("", interface, "Auto-Pause", 1)) {
            ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
                  interface, qe->parent->name);
         } else {
               ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
         }
      }
   }
   return;
}
static int rqm_exec ( struct ast_channel chan,
const char *  data 
) [static]

RemoveQueueMember application.

Definition at line 6727 of file app_queue.c.

References ao2_ref, args, AST_APP_ARG, ast_channel_name(), ast_channel_uniqueid(), ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), find_member_by_queuename_and_interface(), member::interface, LOG_NOTICE, LOG_WARNING, member::membername, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

{
   int res=-1;
   char *parse, *temppos = NULL;
   struct member *mem = NULL;

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(interface);
   );


   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (ast_strlen_zero(args.interface)) {
      args.interface = ast_strdupa(ast_channel_name(chan));
      temppos = strrchr(args.interface, '-');
      if (temppos) {
         *temppos = '\0';
      }
   }

   ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);

   if (log_membername_as_agent) {
      mem = find_member_by_queuename_and_interface(args.queuename, args.interface);
   }

   switch (remove_from_queue(args.queuename, args.interface)) {
   case RES_OKAY:
      if (!mem || ast_strlen_zero(mem->membername)) {
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "REMOVEMEMBER", "%s", "");
      } else {
         ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->membername, "REMOVEMEMBER", "%s", "");
      }
      ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
      pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
      res = 0;
      break;
   case RES_EXISTS:
      ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
      pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
      res = 0;
      break;
   case RES_NOSUCHQUEUE:
      ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
      pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
      res = 0;
      break;
   case RES_NOT_DYNAMIC:
      ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
      pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
      res = 0;
      break;
   }

   if (mem) {
      ao2_ref(mem, -1);
   }

   return res;
}
static void rt_handle_member_record ( struct call_queue q,
char *  interface,
struct ast_config member_config 
) [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 2488 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_copy_string(), ast_false(), ast_log(), ast_queue_log(), ast_strlen_zero(), ast_true(), ast_variable_retrieve(), create_queue_member(), member::dead, member::interface, LOG_WARNING, member_add_to_queue(), member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::ringinuse, call_queue::ringinuse, member::rt_uniqueid, S_OR, and member::state_interface.

Referenced by find_queue_by_name_rt(), and update_realtime_members().

{
   struct member *m;
   struct ao2_iterator mem_iter;
   int penalty = 0;
   int paused  = 0;
   int found = 0;
   int ringinuse = q->ringinuse;

   const char *config_val;
   const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
   const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
   const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
   const char *penalty_str = ast_variable_retrieve(member_config, interface, "penalty");
   const char *paused_str = ast_variable_retrieve(member_config, interface, "paused");

   if (ast_strlen_zero(rt_uniqueid)) {
      ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
      return;
   }

   if (penalty_str) {
      penalty = atoi(penalty_str);
      if ((penalty < 0) && negative_penalty_invalid) {
         return;
      } else if (penalty < 0) {
         penalty = 0;
      }
   }

   if (paused_str) {
      paused = atoi(paused_str);
      if (paused < 0) {
         paused = 0;
      }
   }

   if ((config_val = ast_variable_retrieve(member_config, interface, realtime_ringinuse_field))) {
      if (ast_true(config_val)) {
         ringinuse = 1;
      } else if (ast_false(config_val)) {
         ringinuse = 0;
      } else {
         ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
      }
   }

   /* Find member by realtime uniqueid and update */
   mem_iter = ao2_iterator_init(q->members, 0);
   while ((m = ao2_iterator_next(&mem_iter))) {
      if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
         m->dead = 0;   /* Do not delete this one. */
         ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
         if (paused_str) {
            m->paused = paused;
         }
         if (strcasecmp(state_interface, m->state_interface)) {
            ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
         }
         m->penalty = penalty;
         m->ringinuse = ringinuse;
         found = 1;
         ao2_ref(m, -1);
         break;
      }
      ao2_ref(m, -1);
   }
   ao2_iterator_destroy(&mem_iter);

   /* Create a new member */
   if (!found) {
      if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse))) {
         m->dead = 0;
         m->realtime = 1;
         ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
         if (!log_membername_as_agent) {
            ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
         } else {
            ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
         }
         member_add_to_queue(q, m);
         ao2_ref(m, -1);
         m = NULL;
      }
   }
}
static int say_periodic_announcement ( struct queue_ent qe,
int  ringing 
) [static]

Playback announcement to queued members if period has elapsed.

Definition at line 3909 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(), wait_for_answer(), and wait_our_turn().

{
   int res = 0;
   time_t now;

   /* Get the current time */
   time(&now);

   /* Check to see if it is time to announce */
   if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) {
      return 0;
   }

   /* Stop the music on hold so we can play our own file */
   if (ringing) {
      ast_indicate(qe->chan,-1);
   } else {
      ast_moh_stop(qe->chan);
   }

   ast_verb(3, "Playing periodic announcement\n");

   if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
      qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
   } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce ||
      ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
      qe->last_periodic_announce_sound = 0;
   }

   /* play the announcement */
   res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));

   if (res > 0 && !valid_exit(qe, res)) {
      res = 0;
   }

   /* Resume Music on Hold if the caller is going to stay in the queue */
   if (!res) {
      if (ringing) {
         ast_indicate(qe->chan, AST_CONTROL_RINGING);
      } else {
         ast_moh_start(qe->chan, qe->moh, NULL);
      }
   }

   /* update last_periodic_announce_time */
   if (qe->parent->relativeperiodicannounce) {
      time(&qe->last_periodic_announce_time);
   } else {
      qe->last_periodic_announce_time = now;
   }

   /* Update the current periodic announcement to the next announcement */
   if (!qe->parent->randomperiodicannounce) {
      qe->last_periodic_announce_sound++;
   }

   return res;
}
static int say_position ( struct queue_ent qe,
int  ringing 
) [static]

Definition at line 3078 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_channel_language(), ast_channel_name(), 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, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, 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(), wait_for_answer(), and wait_our_turn().

{
   int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
   int say_thanks = 1;
   time_t now;

   /* Let minannouncefrequency seconds pass between the start of each position announcement */
   time(&now);
   if ((now - qe->last_pos) < qe->parent->minannouncefrequency) {
      return 0;
   }

   /* If either our position has changed, or we are over the freq timer, say position */
   if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) {
      return 0;
   }

   if (ringing) {
      ast_indicate(qe->chan,-1);
   } else {
      ast_moh_stop(qe->chan);
   }

   if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
      qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
      (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
      qe->pos <= qe->parent->announcepositionlimit)) {
         announceposition = 1;
   }


   if (announceposition == 1) {
      /* Say we're next, if we are */
      if (qe->pos == 1) {
         res = play_file(qe->chan, qe->parent->sound_next);
         if (res) {
            goto playout;
         }
         goto posout;
      } else {
         if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
            /* More than Case*/
            res = play_file(qe->chan, qe->parent->queue_quantity1);
            if (res) {
               goto playout;
            }
            res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, ast_channel_language(qe->chan), NULL); /* Needs gender */
            if (res) {
               goto playout;
            }
         } else {
            /* Normal Case */
            res = play_file(qe->chan, qe->parent->sound_thereare);
            if (res) {
               goto playout;
            }
            res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, ast_channel_language(qe->chan), NULL); /* Needs gender */
            if (res) {
               goto playout;
            }
         }
         if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
            /* More than Case*/
            res = play_file(qe->chan, qe->parent->queue_quantity2);
            if (res) {
               goto playout;
            }
         } else {
            res = play_file(qe->chan, qe->parent->sound_calls);
            if (res) {
               goto playout;
            }
         }
      }
   }
   /* Round hold time to nearest minute */
   avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);

   /* If they have specified a rounding then round the seconds as well */
   if (qe->parent->roundingseconds) {
      avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
      avgholdsecs *= qe->parent->roundingseconds;
   } else {
      avgholdsecs = 0;
   }

   ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);

   /* If the hold time is >1 min, if it's enabled, and if it's not
      supposed to be only once and we have already said it, say it */
    if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
        ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
        !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
      res = play_file(qe->chan, qe->parent->sound_holdtime);
      if (res) {
         goto playout;
      }

      if (avgholdmins >= 1) {
         res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, ast_channel_language(qe->chan), NULL);
         if (res) {
            goto playout;
         }

         if (avgholdmins == 1) {
            res = play_file(qe->chan, qe->parent->sound_minute);
            if (res) {
               goto playout;
            }
         } else {
            res = play_file(qe->chan, qe->parent->sound_minutes);
            if (res) {
               goto playout;
            }
         }
      }
      if (avgholdsecs >= 1) {
         res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, ast_channel_language(qe->chan), NULL);
         if (res) {
            goto playout;
         }

         res = play_file(qe->chan, qe->parent->sound_seconds);
         if (res) {
            goto playout;
         }
      }
   } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
      say_thanks = 0;
   }

posout:
   if (qe->parent->announceposition) {
      ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
         ast_channel_name(qe->chan), qe->parent->name, qe->pos);
   }
   if (say_thanks) {
      res = play_file(qe->chan, qe->parent->sound_thanks);
   }
playout:

   if ((res > 0 && !valid_exit(qe, res))) {
      res = 0;
   }

   /* Set our last_pos indicators */
   qe->last_pos = now;
   qe->last_pos_said = qe->pos;

   /* Don't restart music on hold if we're about to exit the caller from the queue */
   if (!res) {
      if (ringing) {
         ast_indicate(qe->chan, AST_CONTROL_RINGING);
      } else {
         ast_moh_start(qe->chan, qe->moh, NULL);
      }
   }
   return res;
}
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 4953 of file app_queue.c.

References AGENT, ast_channel_name(), ast_channel_uniqueid(), CALLER, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, member::interface, manager_event, member::membername, queue_ent::parent, QUEUE_EVENT_VARIABLES, queue_ent::start, TRANSFER, and vars2manager().

Referenced by try_calling().

{
   const char *reason = NULL; /* silence dumb compilers */

   if (!qe->parent->eventwhencalled) {
      return;
   }

   switch (rsn) {
   case CALLER:
      reason = "caller";
      break;
   case AGENT:
      reason = "agent";
      break;
   case TRANSFER:
      reason = "transfer";
      break;
   }

   /*** DOCUMENTATION
   <managerEventInstance>
      <synopsis>Raised when an agent has finished servicing a member in the queue.</synopsis>
      <syntax>
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
         <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
         <parameter name="TalkTime">
            <para>The time the agent talked with the member in the queue, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
         </parameter>
         <parameter name="Reason">
            <enumlist>
               <enum name="caller"/>
               <enum name="agent"/>
               <enum name="transfer"/>
            </enumlist>
         </parameter>
      </syntax>
      <see-also>
         <ref type="managerEvent">AgentCalled</ref>
         <ref type="managerEvent">AgentConnect</ref>
      </see-also>
   </managerEventInstance>
   ***/
   manager_event(EVENT_FLAG_AGENT, "AgentComplete",
      "Queue: %s\r\n"
      "Uniqueid: %s\r\n"
      "Channel: %s\r\n"
      "Member: %s\r\n"
      "MemberName: %s\r\n"
      "HoldTime: %ld\r\n"
      "TalkTime: %ld\r\n"
      "Reason: %s\r\n"
      "%s",
      queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
      (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
}
static int set_member_paused ( const char *  queuename,
const char *  interface,
const char *  reason,
int  paused 
) [static]

Definition at line 6230 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_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), is_member_available(), LOG_WARNING, manager_event, member::membername, call_queue::name, num_available_members(), 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().

{
   int found = 0;
   struct call_queue *q;
   struct member *mem;
   struct ao2_iterator queue_iter;
   int failed;

   /* Special event for when all queues are paused - individual events still generated */
   /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
   if (ast_strlen_zero(queuename))
      ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");

   queue_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
      ao2_lock(q);
      if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
         if ((mem = interface_exists(q, interface))) {
            if (mem->paused == paused) {
               ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
            }

            failed = 0;
            if (mem->realtime) {
               failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
            }

            if (failed) {
               ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
               ao2_ref(mem, -1);
               ao2_unlock(q);
               queue_t_unref(q, "Done with iterator");
               continue;
            }
            found++;
            mem->paused = paused;

            if (queue_persistent_members) {
               dump_queue_members(q);
            }

            if (is_member_available(q, mem)) {
               ast_devstate_changed(AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
            } else if (!num_available_members(q)) {
               ast_devstate_changed(AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, "Queue:%s_avail", q->name);
            }

            ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));

            if (!ast_strlen_zero(reason)) {
               /*** DOCUMENTATION
               <managerEventInstance>
                  <synopsis>Raised when a member is paused/unpaused in the queue with a reason.</synopsis>
                  <syntax>
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Paused'])" />
                     <parameter name="Reason">
                        <para>The reason given for pausing or unpausing a queue member.</para>
                     </parameter>
                  </syntax>
                  <see-also>
                     <ref type="application">PauseQueueMember</ref>
                     <ref type="application">UnPauseQueueMember</ref>
                  </see-also>
               </managerEventInstance>
               ***/
               manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
                  "Queue: %s\r\n"
                  "Location: %s\r\n"
                  "MemberName: %s\r\n"
                  "Paused: %d\r\n"
                  "Reason: %s\r\n",
                     q->name, mem->interface, mem->membername, paused, reason);
            } else {
               /*** DOCUMENTATION
               <managerEventInstance>
                  <synopsis>Raised when a member is paused/unpaused in the queue without a reason.</synopsis>
                  <syntax>
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Paused'])" />
                  </syntax>
                  <see-also>
                     <ref type="application">PauseQueueMember</ref>
                     <ref type="application">UnPauseQueueMember</ref>
                  </see-also>
               </managerEventInstance>
               ***/
               manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
                  "Queue: %s\r\n"
                  "Location: %s\r\n"
                  "MemberName: %s\r\n"
                  "Paused: %d\r\n",
                     q->name, mem->interface, mem->membername, paused);
            }
            ao2_ref(mem, -1);
         }
      }

      if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
         ao2_unlock(q);
         queue_t_unref(q, "Done with iterator");
         break;
      }

      ao2_unlock(q);
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&queue_iter);

   return found ? RESULT_SUCCESS : RESULT_FAILURE;
}
static int set_member_penalty_help_members ( struct call_queue q,
const char *  interface,
int  penalty 
) [static]

Definition at line 6355 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, ast_queue_log(), EVENT_FLAG_AGENT, member::interface, interface_exists(), manager_event, call_queue::name, member::penalty, member::realtime, and update_realtime_member_field().

Referenced by set_member_value_help_members().

{
   struct member *mem;
   int foundinterface = 0;
   char rtpenalty[80];

   ao2_lock(q);
   if ((mem = interface_exists(q, interface))) {
      foundinterface++;
      if (!mem->realtime) {
         mem->penalty = penalty;
      } else {
         sprintf(rtpenalty, "%i", penalty);
         update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
      }
      ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
      /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a member's penalty is changed.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Penalty'])" />
         </syntax>
         <see-also>
            <ref type="function">QUEUE_MEMBER</ref>
         </see-also>
      </managerEventInstance>
      ***/
      manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
         "Queue: %s\r\n"
         "Location: %s\r\n"
         "Penalty: %d\r\n",
         q->name, mem->interface, penalty);
      ao2_ref(mem, -1);
   }
   ao2_unlock(q);

   return foundinterface;
}
static int set_member_ringinuse_help_members ( struct call_queue q,
const char *  interface,
int  ringinuse 
) [static]

Definition at line 6396 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, ast_queue_log(), EVENT_FLAG_AGENT, member::interface, interface_exists(), manager_event, call_queue::name, member::realtime, member::ringinuse, and update_realtime_member_field().

Referenced by set_member_value_help_members().

{
   struct member *mem;
   int foundinterface = 0;
   char rtringinuse[80];

   ao2_lock(q);
   if ((mem = interface_exists(q, interface))) {
      foundinterface++;
      if (!mem->realtime) {
         mem->ringinuse = ringinuse;
      } else {
         sprintf(rtringinuse, "%i", ringinuse);
         update_realtime_member_field(mem, q->name, realtime_ringinuse_field, rtringinuse);
      }
      ast_queue_log(q->name, "NONE", interface, "RINGINUSE", "%d", ringinuse);
      /*** DOCUMENTATION
      <managerEventInstance>
         <synopsis>Raised when a member's ringinuse setting is changed.</synopsis>
         <syntax>
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
            <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Location'])" />
            <parameter name="Ringinuse">
               <enumlist>
                  <enum name="0"/>
                  <enum name="1"/>
               </enumlist>
            </parameter>
         </syntax>
         <see-also>
            <ref type="function">QUEUE_MEMBER</ref>
         </see-also>
      </managerEventInstance>
      ***/
      manager_event(EVENT_FLAG_AGENT, "QueueMemberRinginuse",
         "Queue: %s\r\n"
         "Location: %s\r\n"
         "Ringinuse: %d\r\n",
         q->name, mem->interface, ringinuse);
      ao2_ref(mem, -1);
   }
   ao2_unlock(q);

   return foundinterface;
}
static int set_member_value ( const char *  queuename,
const char *  interface,
int  property,
int  value 
) [static]

Definition at line 6465 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_category_browse(), ast_check_realtime(), ast_load_realtime_multientry(), ast_log(), ast_strlen_zero(), find_load_queue_rt_friendly(), LOG_ERROR, MEMBER_PENALTY, name, queues, RESULT_FAILURE, RESULT_SUCCESS, SENTINEL, and set_member_value_help_members().

Referenced by handle_queue_set_member_penalty(), handle_queue_set_member_ringinuse(), manager_queue_member_penalty(), manager_queue_member_ringinuse(), queue_function_mem_write(), and queue_function_memberpenalty_write().

{
   int foundinterface = 0, foundqueue = 0;
   struct call_queue *q;
   struct ast_config *queue_config = NULL;
   struct ao2_iterator queue_iter;

   /* property dependent restrictions on values should be checked in this switch */
   switch (property) {
   case MEMBER_PENALTY:
      if (value < 0 && !negative_penalty_invalid) {
         ast_log(LOG_ERROR, "Invalid penalty (%d)\n", value);
         return RESULT_FAILURE;
      }
   }

   if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
      if (ast_check_realtime("queues")) {
         char *name;
         queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
         if (queue_config) {
            for (name = ast_category_browse(queue_config, NULL);
                !ast_strlen_zero(name);
                name = ast_category_browse(queue_config, name)) {
               if ((q = find_load_queue_rt_friendly(name))) {
                  foundqueue++;
                  foundinterface += set_member_value_help_members(q, interface, property, value);
               }
            }
         }
      }

      /* After hitting realtime queues, go back and get the regular ones. */
      queue_iter = ao2_iterator_init(queues, 0);
      while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
         foundqueue++;
         foundinterface += set_member_value_help_members(q, interface, property, value);
      }
      ao2_iterator_destroy(&queue_iter);
   } else { /* We actually have a queuename, so we can just act on the single queue. */
      if ((q = find_load_queue_rt_friendly(queuename))) {
         foundqueue++;
         foundinterface += set_member_value_help_members(q, interface, property, value);
      }
   }

   if (foundinterface) {
      return RESULT_SUCCESS;
   } else if (!foundqueue) {
      ast_log (LOG_ERROR, "Invalid queuename\n");
   } else {
      ast_log (LOG_ERROR, "Invalid interface\n");
   }

   return RESULT_FAILURE;
}
static int set_member_value_help_members ( struct call_queue q,
const char *  interface,
int  property,
int  value 
) [static]

Definition at line 6442 of file app_queue.c.

References ast_log(), LOG_ERROR, MEMBER_PENALTY, MEMBER_RINGINUSE, set_member_penalty_help_members(), and set_member_ringinuse_help_members().

Referenced by set_member_value().

{
   switch(property) {
   case MEMBER_PENALTY:
      return set_member_penalty_help_members(q, interface, value);

   case MEMBER_RINGINUSE:
      return set_member_ringinuse_help_members(q, interface, value);

   default:
      ast_log(LOG_ERROR, "Attempted to set invalid property\n");
      return 0;
   }
}
static void set_queue_result ( struct ast_channel chan,
enum queue_result  res 
) [static]

sets the QUEUESTATUS channel variable

Definition at line 1320 of file app_queue.c.

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

Referenced by queue_exec().

{
   int i;

   for (i = 0; i < ARRAY_LEN(queue_results); i++) {
      if (queue_results[i].id == res) {
         pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
         return;
      }
   }
}
static void set_queue_variables ( struct call_queue q,
struct ast_channel chan 
) [static]

Set variables of queue.

Definition at line 1489 of file app_queue.c.

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

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

{
   char interfacevar[256]="";
   float sl = 0;

   ao2_lock(q);

   if (q->setqueuevar) {
      sl = 0;
      if (q->callscompleted > 0) {
         sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
      }

      snprintf(interfacevar, sizeof(interfacevar),
         "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
         q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);

      ao2_unlock(q);

      pbx_builtin_setvar_multiple(chan, interfacevar);
   } else {
      ao2_unlock(q);
   }
}
static struct ast_datastore* setup_transfer_datastore ( struct queue_ent qe,
struct member member,
time_t  starttime,
int  callcompletedinsl 
) [static, read]

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

Definition at line 5084 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_transfer_ds::callcompletedinsl, queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, queue_transfer_info, and queue_transfer_ds::starttime.

Referenced by try_calling().

{
   struct ast_datastore *ds;
   struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));

   if (!qtds) {
      ast_log(LOG_WARNING, "Memory allocation error!\n");
      return NULL;
   }

   ast_channel_lock(qe->chan);
   if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
      ast_channel_unlock(qe->chan);
      ast_free(qtds);
      ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
      return NULL;
   }

   qtds->qe = qe;
   /* This member is refcounted in try_calling, so no need to add it here, too */
   qtds->member = member;
   qtds->starttime = starttime;
   qtds->callcompletedinsl = callcompletedinsl;
   ds->data = qtds;
   ast_channel_datastore_add(qe->chan, ds);
   ast_channel_unlock(qe->chan);
   return ds;
}
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 3885 of file app_queue.c.

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

Referenced by try_calling().

{
   struct callattempt *best = find_best(outgoing);

   if (best) {
      /* Ring just the best channel */
      ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
      qe->linpos = best->metric % 1000;
   } else {
      /* Just increment rrpos */
      if (qe->linwrapped) {
         /* No more channels, start over */
         qe->linpos = 0;
      } else {
         /* Prioritize next entry */
         qe->linpos++;
      }
   }
   qe->linwrapped = 0;

   return 0;
}
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 3861 of file app_queue.c.

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

Referenced by try_calling().

{
   struct callattempt *best = find_best(outgoing);

   if (best) {
      /* Ring just the best channel */
      ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
      qe->parent->rrpos = best->metric % 1000;
   } else {
      /* Just increment rrpos */
      if (qe->parent->wrapped) {
         /* No more channels, start over */
         qe->parent->rrpos = 0;
      } else {
         /* Prioritize next entry */
         qe->parent->rrpos++;
      }
   }
   qe->parent->wrapped = 0;

   return 0;
}
static int strat2int ( const char *  strategy) [static]

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

{
   int x;

   for (x = 0; x < ARRAY_LEN(strategies); x++) {
      if (!strcasecmp(strategy, strategies[x].name)) {
         return strategies[x].strategy;
      }
   }

   return -1;
}
static int try_calling ( struct queue_ent qe,
const struct ast_flags  opts,
char **  opt_args,
char *  announceoverride,
const char *  url,
int *  tries,
int *  noption,
const char *  agi,
const char *  macro,
const char *  gosub,
int  ringing 
) [static]

Definition at line 5168 of file app_queue.c.

References AGENT, queue_ent::announce, ao2_alloc, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_app_exec_macro(), ast_app_exec_sub(), ast_asprintf, ast_autoservice_chan_hangup_peer(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CAUSE_ANSWERED_ELSEWHERE, ast_cdr_append(), ast_cdr_dup(), ast_cdr_failed(), AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_init(), ast_cdr_isset_unanswered(), ast_cdr_reset(), ast_cdr_setdestchan(), ast_channel_caller(), ast_channel_cdr(), ast_channel_connected(), ast_channel_context(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_exten(), ast_channel_hangupcause(), ast_channel_language(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_priority(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_softhangup_internal_flag(), ast_channel_supports_html(), ast_channel_tech(), ast_channel_uniqueid(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_datastore_alloc(), ast_datastore_free(), ast_debug, AST_DIGIT_ANY, ast_exists_extension(), AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_free, ast_goto_if_exists(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_parseable_goto(), ast_party_connected_line_copy(), ast_pbx_start(), ast_queue_log(), ast_random(), ast_replace_subargument_delimiter(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), callattempt::block_connected_update, calc_metric(), callattempt_free(), CALLER, member::calls, queue_ent::cancel_answered_elsewhere, callattempt::chan, queue_ent::chan, queue_end_bridge::chan, callattempt::connected, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, member::dynamic, ast_bridge_config::end_bridge_callback, end_bridge_callback(), ast_bridge_config::end_bridge_callback_data, ast_bridge_config::end_bridge_callback_data_fixup, end_bridge_callback_data_fixup(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_bridge_config::features_callee, ast_bridge_config::features_caller, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, ast_dialed_interface::interface, callattempt::interface, member::interface, callattempt::lastcall, member::lastcall, callattempt::lastqueue, member::lastqueue, leave_queue(), ast_cdr::linkedid, LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event, callattempt::member, call_queue::memberdelay, call_queue::membergosub, call_queue::membermacro, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, call_queue::name, ast_cdr::next, queue_ent::opos, OPT_ARG_CALLEE_GO_ON, OPT_CALLEE_AUTOMIXMON, OPT_CALLEE_AUTOMON, OPT_CALLEE_GO_ON, OPT_CALLEE_HANGUP, OPT_CALLEE_PARK, OPT_CALLEE_TRANSFER, OPT_CALLER_AUTOMIXMON, OPT_CALLER_AUTOMON, OPT_CALLER_HANGUP, OPT_CALLER_PARK, OPT_CALLER_TRANSFER, OPT_DATA_QUALITY, OPT_GO_ON, OPT_IGNORE_CALL_FW, OPT_IGNORE_CONNECTEDLINE, OPT_MARK_AS_ANSWERED, OPT_NO_RETRY, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_multiple(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), member::penalty, queue_ent::pending, play_file(), queue_ent::pos, queue_end_bridge::q, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, queue_t_ref, queue_transfer_info, member::realtime, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), S_COR, send_agent_complete(), call_queue::servicelevel, set_queue_variables(), call_queue::setinterfacevar, call_queue::setqueueentryvar, setup_transfer_datastore(), call_queue::sound_callerannounce, call_queue::sound_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, queue_ent::start, callattempt::stillgoing, store_next_lin(), store_next_rr(), call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, TRANSFER, type, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.

Referenced by queue_exec().

{
   struct member *cur;
   struct callattempt *outgoing = NULL; /* the list of calls we are building */
   int to, orig;
   char oldexten[AST_MAX_EXTENSION]="";
   char oldcontext[AST_MAX_CONTEXT]="";
   char queuename[256]="";
   char interfacevar[256]="";
   struct ast_channel *peer;
   struct ast_channel *which;
   struct callattempt *lpeer;
   struct member *member;
   struct ast_app *application;
   int res = 0, bridge = 0;
   int numbusies = 0;
   int x=0;
   char *announce = NULL;
   char digit = 0;
   time_t callstart;
   time_t now = time(NULL);
   struct ast_bridge_config bridge_config;
   char nondataquality = 1;
   char *agiexec = NULL;
   char *macroexec = NULL;
   char *gosubexec = NULL;
   const char *monitorfilename;
   const char *monitor_exec;
   const char *monitor_options;
   char tmpid[256], tmpid2[256];
   char meid[1024], meid2[1024];
   char mixmonargs[1512];
   struct ast_app *mixmonapp = NULL;
   char *p;
   char vars[2048];
   int forwardsallowed = 1;
   int block_connected_line = 0;
   int callcompletedinsl;
   struct ao2_iterator memi;
   struct ast_datastore *datastore, *transfer_ds;
   struct queue_end_bridge *queue_end_bridge = NULL;

   ast_channel_lock(qe->chan);
   datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
   ast_channel_unlock(qe->chan);

   memset(&bridge_config, 0, sizeof(bridge_config));
   tmpid[0] = 0;
   meid[0] = 0;
   time(&now);

   /* If we've already exceeded our timeout, then just stop
    * This should be extremely rare. queue_exec will take care
    * of removing the caller and reporting the timeout as the reason.
    */
   if (qe->expire && now >= qe->expire) {
      res = 0;
      goto out;
   }

   if (ast_test_flag(&opts, OPT_CALLEE_TRANSFER)) {
      ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
   }
   if (ast_test_flag(&opts, OPT_CALLER_TRANSFER)) {
      ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
   }
   if (ast_test_flag(&opts, OPT_CALLEE_AUTOMON)) {
      ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
   }
   if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
      ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
   }
   if (ast_test_flag(&opts, OPT_GO_ON)) {
      ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
   }
   if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
      nondataquality = 0;
   }
   if (ast_test_flag(&opts, OPT_CALLEE_HANGUP)) {
      ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
   }
   if (ast_test_flag(&opts, OPT_CALLER_HANGUP)) {
      ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
   }
   if (ast_test_flag(&opts, OPT_CALLEE_PARK)) {
      ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
   }
   if (ast_test_flag(&opts, OPT_CALLER_PARK)) {
      ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
   }
   if (ast_test_flag(&opts, OPT_NO_RETRY)) {
      if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR
         || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
         (*tries)++;
      } else {
         *tries = ao2_container_count(qe->parent->members);
      }
      *noption = 1;
   }
   if (ast_test_flag(&opts, OPT_IGNORE_CALL_FW)) {
      forwardsallowed = 0;
   }
   if (ast_test_flag(&opts, OPT_IGNORE_CONNECTEDLINE)) {
      block_connected_line = 1;
   }
   if (ast_test_flag(&opts, OPT_CALLEE_AUTOMIXMON)) {
      ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
   }
   if (ast_test_flag(&opts, OPT_CALLER_AUTOMIXMON)) {
      ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
   }
   if (ast_test_flag(&opts, OPT_MARK_AS_ANSWERED)) {
      qe->cancel_answered_elsewhere = 1;
   }

   /* if the calling channel has AST_CAUSE_ANSWERED_ELSEWHERE set, make sure this is inherited.
      (this is mainly to support chan_local)
   */
   if (ast_channel_hangupcause(qe->chan) == AST_CAUSE_ANSWERED_ELSEWHERE) {
      qe->cancel_answered_elsewhere = 1;
   }

   ao2_lock(qe->parent);
   ast_debug(1, "%s is trying to call a queue member.\n",
                     ast_channel_name(qe->chan));
   ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
   if (!ast_strlen_zero(qe->announce)) {
      announce = qe->announce;
   }
   if (!ast_strlen_zero(announceoverride)) {
      announce = announceoverride;
   }

   memi = ao2_iterator_init(qe->parent->members, 0);
   while ((cur = ao2_iterator_next(&memi))) {
      struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
      struct ast_dialed_interface *di;
      AST_LIST_HEAD(,ast_dialed_interface) *dialed_interfaces;
      if (!tmp) {
         ao2_ref(cur, -1);
         ao2_iterator_destroy(&memi);
         ao2_unlock(qe->parent);
         goto out;
      }
      if (!datastore) {
         if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
            callattempt_free(tmp);
            ao2_ref(cur, -1);
            ao2_iterator_destroy(&memi);
            ao2_unlock(qe->parent);
            goto out;
         }
         datastore->inheritance = DATASTORE_INHERIT_FOREVER;
         if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
            callattempt_free(tmp);
            ao2_ref(cur, -1);
            ao2_iterator_destroy(&memi);
            ao2_unlock(qe->parent);
            goto out;
         }
         datastore->data = dialed_interfaces;
         AST_LIST_HEAD_INIT(dialed_interfaces);

         ast_channel_lock(qe->chan);
         ast_channel_datastore_add(qe->chan, datastore);
         ast_channel_unlock(qe->chan);
      } else
         dialed_interfaces = datastore->data;

      AST_LIST_LOCK(dialed_interfaces);
      AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
         if (!strcasecmp(cur->interface, di->interface)) {
            ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n",
               di->interface);
            break;
         }
      }
      AST_LIST_UNLOCK(dialed_interfaces);

      if (di) {
         callattempt_free(tmp);
         ao2_ref(cur, -1);
         continue;
      }

      /* It is always ok to dial a Local interface.  We only keep track of
       * which "real" interfaces have been dialed.  The Local channel will
       * inherit this list so that if it ends up dialing a real interface,
       * it won't call one that has already been called. */
      if (strncasecmp(cur->interface, "Local/", 6)) {
         if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
            callattempt_free(tmp);
            ao2_ref(cur, -1);
            ao2_iterator_destroy(&memi);
            ao2_unlock(qe->parent);
            goto out;
         }
         strcpy(di->interface, cur->interface);

         AST_LIST_LOCK(dialed_interfaces);
         AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
         AST_LIST_UNLOCK(dialed_interfaces);
      }

      /*
       * Seed the callattempt's connected line information with previously
       * acquired connected line info from the queued channel.  The
       * previously acquired connected line info could have been set
       * through the CONNECTED_LINE dialplan function.
       */
      ast_channel_lock(qe->chan);
      ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(qe->chan));
      ast_channel_unlock(qe->chan);

      tmp->block_connected_update = block_connected_line;
      tmp->stillgoing = 1;
      tmp->member = cur;/* Place the reference for cur into callattempt. */
      tmp->lastcall = cur->lastcall;
      tmp->lastqueue = cur->lastqueue;
      ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
      /* Special case: If we ring everyone, go ahead and ring them, otherwise
         just calculate their metric for the appropriate strategy */
      if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
         /* Put them in the list of outgoing thingies...  We're ready now.
            XXX If we're forcibly removed, these outgoing calls won't get
            hung up XXX */
         tmp->q_next = outgoing;
         outgoing = tmp;
         /* If this line is up, don't try anybody else */
         if (outgoing->chan && (ast_channel_state(outgoing->chan) == AST_STATE_UP))
            break;
      } else {
         callattempt_free(tmp);
      }
   }
   ao2_iterator_destroy(&memi);

   if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
      /* Application arguments have higher timeout priority (behaviour for <=1.6) */
      if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) {
         to = (qe->expire - now) * 1000;
      } else {
         to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
      }
   } else {
      /* Config timeout is higher priority thatn application timeout */
      if (qe->expire && qe->expire<=now) {
         to = 0;
      } else if (qe->parent->timeout) {
         to = qe->parent->timeout * 1000;
      } else {
         to = -1;
      }
   }
   orig = to;
   ++qe->pending;
   ao2_unlock(qe->parent);
   ring_one(qe, outgoing, &numbusies);
   lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
      ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
      forwardsallowed, ringing);
   /* The ast_channel_datastore_remove() function could fail here if the
    * datastore was moved to another channel during a masquerade. If this is
    * the case, don't free the datastore here because later, when the channel
    * to which the datastore was moved hangs up, it will attempt to free this
    * datastore again, causing a crash
    */
   ast_channel_lock(qe->chan);
   if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
      ast_datastore_free(datastore);
   }
   ast_channel_unlock(qe->chan);
   ao2_lock(qe->parent);
   if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
      store_next_rr(qe, outgoing);

   }
   if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
      store_next_lin(qe, outgoing);
   }
   ao2_unlock(qe->parent);
   peer = lpeer ? lpeer->chan : NULL;
   if (!peer) {
      qe->pending = 0;
      if (to) {
         /* Must gotten hung up */
         res = -1;
      } else {
         /* User exited by pressing a digit */
         res = digit;
      }
      if (res == -1) {
         ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
      }
      if (ast_cdr_isset_unanswered()) {
         /* channel contains the name of one of the outgoing channels
            in its CDR; zero out this CDR to avoid a dual-posting */
         struct callattempt *o;
         for (o = outgoing; o; o = o->q_next) {
            if (!o->chan) {
               continue;
            }
            if (strcmp(ast_channel_cdr(o->chan)->dstchannel, ast_channel_cdr(qe->chan)->dstchannel) == 0) {
               ast_set_flag(ast_channel_cdr(o->chan), AST_CDR_FLAG_POST_DISABLED);
               break;
            }
         }
      }
   } else { /* peer is valid */
      /* These variables are used with the F option without arguments (callee jumps to next priority after queue) */
      char *caller_context;
      char *caller_extension;
      int caller_priority;

      /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
         we will always return with -1 so that it is hung up properly after the
         conversation.  */
      if (!strcmp(ast_channel_tech(qe->chan)->type, "DAHDI")) {
         ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
      }
      if (!strcmp(ast_channel_tech(peer)->type, "DAHDI")) {
         ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
      }
      /* Update parameters for the queue */
      time(&now);
      recalc_holdtime(qe, (now - qe->start));
      ao2_lock(qe->parent);
      callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
      ao2_unlock(qe->parent);
      member = lpeer->member;
      /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
      ao2_ref(member, 1);
      hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
      outgoing = NULL;
      if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
         int res2;

         res2 = ast_autoservice_start(qe->chan);
         if (!res2) {
            if (qe->parent->memberdelay) {
               ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
               res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
            }
            if (!res2 && announce) {
               if (play_file(peer, announce) < 0) {
                  ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, ast_channel_name(peer));
               }
            }
            if (!res2 && qe->parent->reportholdtime) {
               if (!play_file(peer, qe->parent->sound_reporthold)) {
                  int holdtime, holdtimesecs;

                  time(&now);
                  holdtime = abs((now - qe->start) / 60);
                  holdtimesecs = abs((now - qe->start) % 60);
                  if (holdtime > 0) {
                     ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), NULL);
                     if (play_file(peer, qe->parent->sound_minutes) < 0) {
                        ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, ast_channel_name(peer));
                     }
                  }
                  if (holdtimesecs > 1) {
                     ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer), NULL);
                     if (play_file(peer, qe->parent->sound_seconds) < 0) {
                        ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, ast_channel_name(peer));
                     }
                  }
               }
            }
            ast_autoservice_stop(qe->chan);
         }
         if (ast_check_hangup(peer)) {
            /* Agent must have hung up */
            ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
            ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
            if (qe->parent->eventwhencalled)
               /*** DOCUMENTATION
               <managerEventInstance>
                  <synopsis>Raised when an agent hangs up on a member in the queue.</synopsis>
                  <syntax>
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
                     <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
                  </syntax>
                  <see-also>
                     <ref type="managerEvent">AgentCalled</ref>
                     <ref type="managerEvent">AgentConnect</ref>
                  </see-also>
               </managerEventInstance>
               ***/
               manager_event(EVENT_FLAG_AGENT, "AgentDump",
                     "Queue: %s\r\n"
                     "Uniqueid: %s\r\n"
                     "Channel: %s\r\n"
                     "Member: %s\r\n"
                     "MemberName: %s\r\n"
                     "%s",
                     queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
                     qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
            ast_autoservice_chan_hangup_peer(qe->chan, peer);
            ao2_ref(member, -1);
            goto out;
         } else if (ast_check_hangup(qe->chan)) {
            /* Caller must have hung up just before being connected */
            ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
            ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
            record_abandoned(qe);
            ast_autoservice_chan_hangup_peer(qe->chan, peer);
            ao2_ref(member, -1);
            return -1;
         }
      }
      /* Stop music on hold */
      if (ringing) {
         ast_indicate(qe->chan,-1);
      } else {
         ast_moh_stop(qe->chan);
      }
      /* If appropriate, log that we have a destination channel */
      if (ast_channel_cdr(qe->chan)) {
         ast_cdr_setdestchan(ast_channel_cdr(qe->chan), ast_channel_name(peer));
      }
      /* Make sure channels are compatible */
      res = ast_channel_make_compatible(qe->chan, peer);
      if (res < 0) {
         ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
         ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->chan), ast_channel_name(peer));
         record_abandoned(qe);
         ast_cdr_failed(ast_channel_cdr(qe->chan));
         ast_autoservice_chan_hangup_peer(qe->chan, peer);
         ao2_ref(member, -1);
         return -1;
      }

      /* Play announcement to the caller telling it's his turn if defined */
      if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
         if (play_file(qe->chan, qe->parent->sound_callerannounce)) {
            ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
         }
      }

      ao2_lock(qe->parent);
      /* if setinterfacevar is defined, make member variables available to the channel */
      /* use  pbx_builtin_setvar to set a load of variables with one call */
      if (qe->parent->setinterfacevar) {
         snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
            member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
         pbx_builtin_setvar_multiple(qe->chan, interfacevar);
         pbx_builtin_setvar_multiple(peer, interfacevar);
      }

      /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
      /* use  pbx_builtin_setvar to set a load of variables with one call */
      if (qe->parent->setqueueentryvar) {
         snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
            (long) time(NULL) - qe->start, qe->opos);
         pbx_builtin_setvar_multiple(qe->chan, interfacevar);
         pbx_builtin_setvar_multiple(peer, interfacevar);
      }

      ao2_unlock(qe->parent);

      /* try to set queue variables if configured to do so*/
      set_queue_variables(qe->parent, qe->chan);
      set_queue_variables(qe->parent, peer);

      ast_channel_lock(qe->chan);
      /* Copy next destination data for 'F' option (no args) */
      caller_context = ast_strdupa(ast_channel_context(qe->chan));
      caller_extension = ast_strdupa(ast_channel_exten(qe->chan));
      caller_priority = ast_channel_priority(qe->chan);
      if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
            monitorfilename = ast_strdupa(monitorfilename);
      }
      ast_channel_unlock(qe->chan);

      /* Begin Monitoring */
      if (qe->parent->monfmt && *qe->parent->monfmt) {
         if (!qe->parent->montype) {
            const char *monexec;
            ast_debug(1, "Starting Monitor as requested.\n");
            ast_channel_lock(qe->chan);
            if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
               which = qe->chan;
               monexec = monexec ? ast_strdupa(monexec) : NULL;
            } else {
               which = peer;
            }
            ast_channel_unlock(qe->chan);
            if (monitorfilename) {
               ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
            } else if (ast_channel_cdr(qe->chan)) {
               ast_monitor_start(which, qe->parent->monfmt, ast_channel_cdr(qe->chan)->uniqueid, 1, X_REC_IN | X_REC_OUT);
            } else {
               /* Last ditch effort -- no CDR, make up something */
               snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
               ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
            }
            if (!ast_strlen_zero(monexec)) {
               ast_monitor_setjoinfiles(which, 1);
            }
         } else {
            mixmonapp = pbx_findapp("MixMonitor");

            if (mixmonapp) {
               ast_debug(1, "Starting MixMonitor as requested.\n");
               if (!monitorfilename) {
                  if (ast_channel_cdr(qe->chan)) {
                     ast_copy_string(tmpid, ast_channel_cdr(qe->chan)->uniqueid, sizeof(tmpid));
                  } else {
                     snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
                  }
               } else {
                  const char *m = monitorfilename;
                  for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
                     switch (*m) {
                     case '^':
                        if (*(m + 1) == '{')
                           *p = '$';
                        break;
                     case ',':
                        *p++ = '\\';
                        /* Fall through */
                     default:
                        *p = *m;
                     }
                     if (*m == '\0')
                        break;
                  }
                  if (p == tmpid2 + sizeof(tmpid2))
                     tmpid2[sizeof(tmpid2) - 1] = '\0';

                  pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
               }

               ast_channel_lock(qe->chan);
               if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
                     monitor_exec = ast_strdupa(monitor_exec);
               }
               if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
                     monitor_options = ast_strdupa(monitor_options);
               } else {
                  monitor_options = "";
               }
               ast_channel_unlock(qe->chan);

               if (monitor_exec) {
                  const char *m = monitor_exec;
                  for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
                     switch (*m) {
                     case '^':
                        if (*(m + 1) == '{')
                           *p = '$';
                        break;
                     case ',':
                        *p++ = '\\';
                        /* Fall through */
                     default:
                        *p = *m;
                     }
                     if (*m == '\0') {
                        break;
                     }
                  }
                  if (p == meid2 + sizeof(meid2)) {
                     meid2[sizeof(meid2) - 1] = '\0';
                  }

                  pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
               }

               snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);

               if (!ast_strlen_zero(monitor_exec)) {
                  snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
               } else {
                  snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
               }

               ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
               /* We purposely lock the CDR so that pbx_exec does not update the application data */
               if (ast_channel_cdr(qe->chan)) {
                  ast_set_flag(ast_channel_cdr(qe->chan), AST_CDR_FLAG_LOCKED);
               }
               pbx_exec(qe->chan, mixmonapp, mixmonargs);
               if (ast_channel_cdr(qe->chan)) {
                  ast_clear_flag(ast_channel_cdr(qe->chan), AST_CDR_FLAG_LOCKED);
               }
            } else {
               ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
            }
         }
      }
      /* Drop out of the queue at this point, to prepare for next caller */
      leave_queue(qe);
      if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
         ast_debug(1, "app_queue: sendurl=%s.\n", url);
         ast_channel_sendurl(peer, url);
      }

      /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
      /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
      if (!ast_strlen_zero(macro)) {
         macroexec = ast_strdupa(macro);
      } else {
         if (qe->parent->membermacro) {
            macroexec = ast_strdupa(qe->parent->membermacro);
         }
      }

      if (!ast_strlen_zero(macroexec)) {
         ast_debug(1, "app_queue: macro=%s.\n", macroexec);
         ast_app_exec_macro(qe->chan, peer, macroexec);
      }

      /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
      /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
      if (!ast_strlen_zero(gosub)) {
         gosubexec = ast_strdupa(gosub);
      } else {
         if (qe->parent->membergosub) {
            gosubexec = ast_strdupa(qe->parent->membergosub);
         }
      }

      if (!ast_strlen_zero(gosubexec)) {
         char *gosub_args = NULL;
         char *gosub_argstart;

         ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);

         gosub_argstart = strchr(gosubexec, ',');
         if (gosub_argstart) {
            const char *what_is_s = "s";
            *gosub_argstart = 0;
            if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
                ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
               what_is_s = "~~s~~";
            }
            if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
               gosub_args = NULL;
            }
            *gosub_argstart = ',';
         } else {
            const char *what_is_s = "s";
            if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL)) &&
                ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
               what_is_s = "~~s~~";
            }
            if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
               gosub_args = NULL;
            }
         }
         if (gosub_args) {
            ast_app_exec_sub(qe->chan, peer, gosub_args, 0);
            ast_free(gosub_args);
         } else {
            ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
         }
      }

      if (!ast_strlen_zero(agi)) {
         ast_debug(1, "app_queue: agi=%s.\n", agi);
         application = pbx_findapp("agi");
         if (application) {
            agiexec = ast_strdupa(agi);
            pbx_exec(qe->chan, application, agiexec);
         } else {
            ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
         }
      }
      qe->handled++;
      ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, ast_channel_uniqueid(peer),
                                       (long)(orig - to > 0 ? (orig - to) / 1000 : 0));

      if (ast_channel_cdr(qe->chan)) {
         struct ast_cdr *cdr;
         struct ast_cdr *newcdr;

         /* Only work with the last CDR in the stack*/
         cdr = ast_channel_cdr(qe->chan);
         while (cdr->next) {
            cdr = cdr->next;
         }

         /* If this CDR is not related to us add new one*/
         if ((strcasecmp(cdr->uniqueid, ast_channel_uniqueid(qe->chan))) &&
             (strcasecmp(cdr->linkedid, ast_channel_uniqueid(qe->chan))) &&
             (newcdr = ast_cdr_dup(cdr))) {
            ast_channel_lock(qe->chan);
            ast_cdr_init(newcdr, qe->chan);
            ast_cdr_reset(newcdr, 0);
            cdr = ast_cdr_append(cdr, newcdr);
            cdr = cdr->next;
            ast_channel_unlock(qe->chan);
         }

         if (update_cdr) {
            ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
         }
      }

      if (qe->parent->eventwhencalled)
         /*** DOCUMENTATION
         <managerEventInstance>
            <synopsis>Raised when an agent answers and is bridged to a member in the queue.</synopsis>
            <syntax>
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='Queue'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='Member'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueMemberStatus']/managerEventInstance/syntax/parameter[@name='MemberName'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentRingNoAnswer']/managerEventInstance/syntax/parameter[@name='RingTime'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='QueueCallerAbandon']/managerEventInstance/syntax/parameter[@name='HoldTime'])" />
               <xi:include xpointer="xpointer(/docs/managerEvent[@name='AgentCalled']/managerEventInstance/syntax/parameter[@name='Variable'])" />
            </syntax>
            <see-also>
               <ref type="managerEvent">AgentCalled</ref>
               <ref type="managerEvent">AgentComplete</ref>
               <ref type="managerEvent">AgentDump</ref>
            </see-also>
         </managerEventInstance>
         ***/
         manager_event(EVENT_FLAG_AGENT, "AgentConnect",
               "Queue: %s\r\n"
               "Uniqueid: %s\r\n"
               "Channel: %s\r\n"
               "Member: %s\r\n"
               "MemberName: %s\r\n"
               "HoldTime: %ld\r\n"
               "BridgedChannel: %s\r\n"
               "RingTime: %ld\r\n"
               "%s",
               queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
               (long) time(NULL) - qe->start, ast_channel_uniqueid(peer), (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
               qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
      ast_copy_string(oldcontext, ast_channel_context(qe->chan), sizeof(oldcontext));
      ast_copy_string(oldexten, ast_channel_exten(qe->chan), sizeof(oldexten));

      if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
         queue_end_bridge->q = qe->parent;
         queue_end_bridge->chan = qe->chan;
         bridge_config.end_bridge_callback = end_bridge_callback;
         bridge_config.end_bridge_callback_data = queue_end_bridge;
         bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
         /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
          * to make sure to increase the refcount of this queue so it cannot be freed until we
          * are done with it. We remove this reference in end_bridge_callback.
          */
         queue_t_ref(qe->parent, "For bridge_config reference");
      }

      time(&callstart);
      transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
      bridge = ast_bridge_call(qe->chan, peer, &bridge_config);

      /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
       * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
       * the AgentComplete manager event
       */
      ast_channel_lock(qe->chan);
      if (!attended_transfer_occurred(qe->chan)) {
         struct ast_datastore *tds;

         /* detect a blind transfer */
         if (!(ast_channel_softhangup_internal_flag(qe->chan) | ast_channel_softhangup_internal_flag(peer)) && (strcasecmp(oldcontext, ast_channel_context(qe->chan)) || strcasecmp(oldexten, ast_channel_exten(qe->chan)))) {
            ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
               ast_channel_exten(qe->chan), ast_channel_context(qe->chan), (long) (callstart - qe->start),
               (long) (time(NULL) - callstart), qe->opos);
            send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
         } else if (ast_check_hangup(qe->chan) && !ast_check_hangup(peer)) {
            ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "COMPLETECALLER", "%ld|%ld|%d",
               (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
            send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
         } else {
            ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
               (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
            send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
         }
         if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {
            ast_channel_datastore_remove(qe->chan, tds);
         }
         ast_channel_unlock(qe->chan);
         update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
      } else {
         ast_channel_unlock(qe->chan);

         /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
         send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
      }

      if (transfer_ds) {
         ast_datastore_free(transfer_ds);
      }

      if (!ast_check_hangup(peer) && ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
         int goto_res;

         if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
            ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
            goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
         } else { /* F() */
            goto_res = ast_goto_if_exists(peer, caller_context, caller_extension,
               caller_priority + 1);
         }
         if (goto_res || ast_pbx_start(peer)) {
            ast_autoservice_chan_hangup_peer(qe->chan, peer);
         }
      } else {
         ast_autoservice_chan_hangup_peer(qe->chan, peer);
      }

      res = bridge ? bridge : 1;
      ao2_ref(member, -1);
   }
out:
   hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);

   return res;
}
static int unload_module ( void  ) [static]

Definition at line 9874 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_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, 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.

{
   int res;
   struct ao2_iterator q_iter;
   struct call_queue *q = NULL;

   ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
   res = ast_manager_unregister("QueueStatus");
   res |= ast_manager_unregister("Queues");
   res |= ast_manager_unregister("QueueRule");
   res |= ast_manager_unregister("QueueSummary");
   res |= ast_manager_unregister("QueueAdd");
   res |= ast_manager_unregister("QueueRemove");
   res |= ast_manager_unregister("QueuePause");
   res |= ast_manager_unregister("QueueLog");
   res |= ast_manager_unregister("QueuePenalty");
   res |= ast_manager_unregister("QueueReload");
   res |= ast_manager_unregister("QueueReset");
   res |= ast_manager_unregister("QueueMemberRingInUse");
   res |= ast_unregister_application(app_aqm);
   res |= ast_unregister_application(app_rqm);
   res |= ast_unregister_application(app_pqm);
   res |= ast_unregister_application(app_upqm);
   res |= ast_unregister_application(app_ql);
   res |= ast_unregister_application(app);
   res |= ast_custom_function_unregister(&queueexists_function);
   res |= ast_custom_function_unregister(&queuevar_function);
   res |= ast_custom_function_unregister(&queuemembercount_function);
   res |= ast_custom_function_unregister(&queuemembercount_dep);
   res |= ast_custom_function_unregister(&queuememberlist_function);
   res |= ast_custom_function_unregister(&queuewaitingcount_function);
   res |= ast_custom_function_unregister(&queuememberpenalty_function);

   res |= ast_data_unregister(NULL);

   if (device_state_sub)
      ast_event_unsubscribe(device_state_sub);

   ast_extension_state_del(0, extension_state_cb);

   q_iter = ao2_iterator_init(queues, 0);
   while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
      queues_t_unlink(queues, q, "Remove queue from container due to unload");
      queue_t_unref(q, "Done with iterator");
   }
   ao2_iterator_destroy(&q_iter);
   devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
   ao2_ref(queues, -1);
   ast_unload_realtime("queue_members");
   return res;
}
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 4681 of file app_queue.c.

References ast_channel_name(), 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, pbx_builtin_setvar_helper(), queue_ent::pr, and penalty_rule::time.

Referenced by queue_exec(), and wait_our_turn().

{
   int max_penalty = INT_MAX;

   if (qe->max_penalty != INT_MAX) {
      char max_penalty_str[20];

      if (qe->pr->max_relative) {
         max_penalty = qe->max_penalty + qe->pr->max_value;
      } else {
         max_penalty = qe->pr->max_value;
      }

      /* a relative change to the penalty could put it below 0 */
      if (max_penalty < 0) {
         max_penalty = 0;
      }

      snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
      pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
      qe->max_penalty = max_penalty;
      ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
         qe->max_penalty, ast_channel_name(qe->chan), qe->pr->time);
   }

   if (qe->min_penalty != INT_MAX) {
      char min_penalty_str[20];
      int min_penalty;

      if (qe->pr->min_relative) {
         min_penalty = qe->min_penalty + qe->pr->min_value;
      } else {
         min_penalty = qe->pr->min_value;
      }

      /* a relative change to the penalty could put it below 0 */
      if (min_penalty < 0) {
         min_penalty = 0;
      }

      if (max_penalty != INT_MAX && min_penalty > max_penalty) {
         min_penalty = max_penalty;
      }

      snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
      pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
      qe->min_penalty = min_penalty;
      ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
         qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
   }

   qe->pr = AST_LIST_NEXT(qe->pr, list);
}
static int update_queue ( struct call_queue q,
struct member member,
int  callcompletedinsl,
int  newtalktime 
) [static]

update the queue status

Return values:
Always0

Definition at line 4824 of file app_queue.c.

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

Referenced by queue_transfer_fixup(), and try_calling().

{
   int oldtalktime;

   struct member *mem;
   struct call_queue *qtmp;
   struct ao2_iterator queue_iter;

   if (shared_lastcall) {
      queue_iter = ao2_iterator_init(queues, 0);
      while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
         ao2_lock(qtmp);
         if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
            time(&mem->lastcall);
            mem->calls++;
            mem->lastqueue = q;
            ao2_ref(mem, -1);
         }
         ao2_unlock(qtmp);
         queue_t_unref(qtmp, "Done with iterator");
      }
      ao2_iterator_destroy(&queue_iter);
   } else {
      ao2_lock(q);
      time(&member->lastcall);
      member->calls++;
      member->lastqueue = q;
      ao2_unlock(q);
   }
   ao2_lock(q);
   q->callscompleted++;
   if (callcompletedinsl) {
      q->callscompletedinsl++;
   }
   /* Calculate talktime using the same exponential average as holdtime code*/
   oldtalktime = q->talktime;
   q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
   ao2_unlock(q);
   return 0;
}
static int update_realtime_member_field ( struct member mem,
const char *  queue_name,
const char *  field,
const char *  value 
) [static]

Definition at line 2830 of file app_queue.c.

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

Referenced by queue_function_mem_write(), remove_from_queue(), set_member_paused(), set_member_penalty_help_members(), and set_member_ringinuse_help_members().

{
   int ret = -1;

   if (ast_strlen_zero(mem->rt_uniqueid)) {
      return ret;
   }

   if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) {
      ret = 0;
   }

   return ret;
}
static void update_realtime_members ( struct call_queue q) [static]

Definition at line 2846 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_strlen_zero(), member::dead, member::interface, member_remove_from_queue(), member::membername, call_queue::members, call_queue::name, member::realtime, rt_handle_member_record(), and SENTINEL.

Referenced by find_load_queue_rt_friendly(), and queue_exec().

{
   struct ast_config *member_config = NULL;
   struct member *m;
   char *interface = NULL;
   struct ao2_iterator mem_iter;

   if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
      /* This queue doesn't have realtime members. If the queue still has any realtime
       * members in memory, they need to be removed.
       */
      ao2_lock(q);
      mem_iter = ao2_iterator_init(q->members, 0);
      while ((m = ao2_iterator_next(&mem_iter))) {
         if (m->realtime) {
            member_remove_from_queue(q, m);
         }
         ao2_ref(m, -1);
      }
      ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
      ao2_unlock(q);
      return;
   }

   ao2_lock(q);

   /* Temporarily set realtime  members dead so we can detect deleted ones.*/
   mem_iter = ao2_iterator_init(q->members, 0);
   while ((m = ao2_iterator_next(&mem_iter))) {
      if (m->realtime) {
         m->dead = 1;
      }
      ao2_ref(m, -1);
   }
   ao2_iterator_destroy(&mem_iter);

   while ((interface = ast_category_browse(member_config, interface))) {
      rt_handle_member_record(q, interface, member_config);
   }

   /* Delete all realtime members that have been deleted in DB. */
   mem_iter = ao2_iterator_init(q->members, 0);
   while ((m = ao2_iterator_next(&mem_iter))) {
      if (m->dead) {
         if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
            ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
         } else {
            ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
         }
         member_remove_from_queue(q, m);
      }
      ao2_ref(m, -1);
   }
   ao2_iterator_destroy(&mem_iter);
   ao2_unlock(q);
   ast_config_destroy(member_config);
}
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 1626 of file app_queue.c.

References EVENT_FLAG_AGENT, manager_event, and status.

Referenced by extension_state_cb(), and handle_statechange().

{
   m->status = status;

   if (q->maskmemberstatus) {
      return 0;
   }

   /*** DOCUMENTATION
   <managerEventInstance>
      <synopsis>Raised when a Queue member's status has changed.</synopsis>
      <syntax>
         <parameter name="Queue">
            <para>The name of the queue.</para>
         </parameter>
         <parameter name="Location">
            <para>The queue member's channel technology or location.</para>
         </parameter>
         <parameter name="MemberName">
            <para>The name of the queue member.</para>
         </parameter>
         <parameter name="StateInterface">
            <para>Channel technology or location from which to read device state changes.</para>
         </parameter>
         <parameter name="Membership">
            <enumlist>
               <enum name="dynamic"/>
               <enum name="realtime"/>
               <enum name="static"/>
            </enumlist>
         </parameter>
         <parameter name="Penalty">
            <para>The penalty associated with the queue member.</para>
         </parameter>
         <parameter name="CallsTaken">
            <para>The number of calls this queue member has serviced.</para>
         </parameter>
         <parameter name="LastCall">
            <para>The time this member last took call, expressed in seconds since 00:00, Jan 1, 1970 UTC.</para>
         </parameter>
         <parameter name="Status">
            <para>The numeric device state status of the queue member.</para>
            <enumlist>
               <enum name="0"><para>AST_DEVICE_UNKNOWN</para></enum>
               <enum name="1"><para>AST_DEVICE_NOT_INUSE</para></enum>
               <enum name="2"><para>AST_DEVICE_INUSE</para></enum>
               <enum name="3"><para>AST_DEVICE_BUSY</para></enum>
               <enum name="4"><para>AST_DEVICE_INVALID</para></enum>
               <enum name="5"><para>AST_DEVICE_UNAVAILABLE</para></enum>
               <enum name="6"><para>AST_DEVICE_RINGING</para></enum>
               <enum name="7"><para>AST_DEVICE_RINGINUSE</para></enum>
               <enum name="8"><para>AST_DEVICE_ONHOLD</para></enum>
            </enumlist>
         </parameter>
         <parameter name="Paused">
            <enumlist>
               <enum name="0"/>
               <enum name="1"/>
            </enumlist>
         </parameter>
      </syntax>
   </managerEventInstance>
   ***/
   manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
      "Queue: %s\r\n"
      "Location: %s\r\n"
      "MemberName: %s\r\n"
      "StateInterface: %s\r\n"
      "Membership: %s\r\n"
      "Penalty: %d\r\n"
      "CallsTaken: %d\r\n"
      "LastCall: %d\r\n"
      "Status: %d\r\n"
      "Paused: %d\r\n",
      q->name, m->interface, m->membername, m->state_interface, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
      m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
   );

   return 0;
}
static int upqm_exec ( struct ast_channel chan,
const char *  data 
) [static]

UnPauseQueueMember application.

Definition at line 6691 of file app_queue.c.

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

Referenced by load_module().

{
   char *parse;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(queuename);
      AST_APP_ARG(interface);
      AST_APP_ARG(options);
      AST_APP_ARG(reason);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (ast_strlen_zero(args.interface)) {
      ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
      return -1;
   }

   if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
      ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
      pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
      return 0;
   }

   pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");

   return 0;
}
static int valid_exit ( struct queue_ent qe,
char  digit 
) [static]

Check for valid exit from queue via goto.

Return values:
0if failure
1if successful

Definition at line 3043 of file app_queue.c.

References ast_canmatch_extension(), ast_channel_caller(), ast_goto_if_exists(), ast_strlen_zero(), 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(), wait_a_bit(), wait_for_answer(), and wait_our_turn().

{
   int digitlen = strlen(qe->digits);

   /* Prevent possible buffer overflow */
   if (digitlen < sizeof(qe->digits) - 2) {
      qe->digits[digitlen] = digit;
      qe->digits[digitlen + 1] = '\0';
   } else {
      qe->digits[0] = '\0';
      return 0;
   }

   /* If there's no context to goto, short-circuit */
   if (ast_strlen_zero(qe->context)) {
      return 0;
   }

   /* If the extension is bad, then reset the digits to blank */
   if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
      S_COR(ast_channel_caller(qe->chan)->id.number.valid, ast_channel_caller(qe->chan)->id.number.str, NULL))) {
      qe->digits[0] = '\0';
      return 0;
   }

   /* We have an exact match */
   if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
      qe->valid_digits = 1;
      /* Return 1 on a successful goto */
      return 1;
   }

   return 0;
}
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 3458 of file app_queue.c.

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

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

{
   struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
   const char *tmp;

   if (pbx_builtin_serialize_variables(chan, &buf)) {
      int i, j;

      /* convert "\n" to "\nVariable: " */
      strcpy(vars, "Variable: ");
      tmp = ast_str_buffer(buf);

      for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
         vars[j] = tmp[i];

         if (tmp[i + 1] == '\0') {
            break;
         }
         if (tmp[i] == '\n') {
            vars[j++] = '\r';
            vars[j++] = '\n';

            ast_copy_string(&(vars[j]), "Variable: ", len - j);
            j += 9;
         }
      }
      if (j > len - 3) {
         j = len - 3;
      }
      vars[j++] = '\r';
      vars[j++] = '\n';
      vars[j] = '\0';
   } else {
      /* there are no channel variables; leave it blank */
      *vars = '\0';
   }
   return vars;
}
static int wait_a_bit ( struct queue_ent qe) [static]

Definition at line 5988 of file app_queue.c.

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

Referenced by queue_exec().

{
   /* Don't need to hold the lock while we setup the outgoing calls */
   int retrywait = qe->parent->retry * 1000;

   int res = ast_waitfordigit(qe->chan, retrywait);
   if (res > 0 && !valid_exit(qe, res)) {
      res = 0;
   }

   return res;
}
static struct callattempt* wait_for_answer ( struct queue_ent qe,
struct callattempt outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect,
int  forwardsallowed,
int  ringing 
) [static, read]

Wait for a member to answer the call.

Parameters:
[in]qethe queue_ent corresponding to the caller in the queue
[in]outgoingthe list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero
[in]tothe amount of time (in milliseconds) to wait for a response
[out]digitif a user presses a digit to exit the queue, this is the digit the caller pressed
[in]prebusiesnumber of busy members calculated prior to calling wait_for_answer
[in]caller_disconnectif the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call
[in]forwardsallowedused 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()

Definition at line 4100 of file app_queue.c.

References call_queue::announce_to_first_user, call_queue::announcefrequency, callattempt::aoc_s_rate_list, ast_aoc_decode(), ast_aoc_destroy_decoded(), ast_aoc_destroy_encoded(), ast_aoc_encode(), ast_aoc_get_msg_type(), AST_AOC_S, ast_call(), ast_cdr_busy(), AST_CEL_FORWARD, ast_cel_report_event(), ast_channel_accountcode(), ast_channel_call_forward(), ast_channel_caller(), ast_channel_cdr(), ast_channel_connected(), ast_channel_connected_line_macro(), ast_channel_connected_line_sub(), ast_channel_context(), ast_channel_datastore_inherit(), ast_channel_dialed(), ast_channel_exten(), ast_channel_hangupcause_set(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, ast_channel_macroexten(), AST_CHANNEL_NAME, ast_channel_name(), ast_channel_nativeformats(), ast_channel_redirecting(), ast_channel_redirecting_macro(), ast_channel_redirecting_sub(), ast_channel_unlock, ast_channel_update_connected_line(), ast_channel_update_redirecting(), ast_connected_line_copy_from_caller(), ast_connected_line_parse_data(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_PVT_CAUSE_CODE, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_WATCHERS, ast_moh_stop(), ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_party_connected_line_set(), ast_party_connected_line_set_init(), ast_party_number_free(), ast_party_number_init(), ast_party_redirecting_copy(), ast_party_redirecting_free(), ast_party_redirecting_init(), ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), ast_remaining_ms(), ast_request(), AST_STATE_UP, ast_strdup, ast_strlen_zero(), ast_tvnow(), ast_verb, ast_waitfor_n(), call_queue::autopausebusy, call_queue::autopauseunavail, callattempt::block_connected_update, callattempt::call_next, callattempt::chan, queue_ent::chan, callattempt::connected, ast_frame::data, ast_frame::datalen, callattempt::dial_callerid_absent, do_hang(), f, ast_frame::frametype, ast_party_redirecting::from, ast_party_caller::id, ast_frame_subclass::integer, callattempt::interface, member::interface, LOG_NOTICE, callattempt::member, member::membername, call_queue::name, ast_party_id::number, queue_ent::parent, callattempt::pending_connected_update, call_queue::periodicannouncefrequency, queue_ent::pos, ast_frame::ptr, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), queue_ent::ring_when_ringing, rna(), S_OR, say_periodic_announcement(), say_position(), ast_party_connected_line::source, queue_ent::start, status, callattempt::stillgoing, ast_party_number::str, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, ast_party_dialed::transit_network_select, ast_frame::uint32, ast_party_number::valid, and valid_exit().

Referenced by try_calling().

{
   const char *queue = qe->parent->name;
   struct callattempt *o, *start = NULL, *prev = NULL;
   int status;
   int numbusies = prebusies;
   int numnochan = 0;
   int stillgoing = 0;
   int orig = *to;
   struct ast_frame *f;
   struct callattempt *peer = NULL;
   struct ast_channel *winner;
   struct ast_channel *in = qe->chan;
   char on[80] = "";
   char membername[80] = "";
   long starttime = 0;
   long endtime = 0;
#ifdef HAVE_EPOLL
   struct callattempt *epollo;
#endif
   struct ast_party_connected_line connected_caller;
   char *inchan_name;
   struct timeval start_time_tv = ast_tvnow();

   ast_party_connected_line_init(&connected_caller);

   ast_channel_lock(qe->chan);
   inchan_name = ast_strdupa(ast_channel_name(qe->chan));
   ast_channel_unlock(qe->chan);

   starttime = (long) time(NULL);
#ifdef HAVE_EPOLL
   for (epollo = outgoing; epollo; epollo = epollo->q_next) {
      if (epollo->chan) {
         ast_poll_channel_add(in, epollo->chan);
      }
   }
#endif

   while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
      int numlines, retry, pos = 1;
      struct ast_channel *watchers[AST_MAX_WATCHERS];
      watchers[0] = in;
      start = NULL;

      for (retry = 0; retry < 2; retry++) {
         numlines = 0;
         for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
            if (o->stillgoing) { /* Keep track of important channels */
               stillgoing = 1;
               if (o->chan) {
                  if (pos < AST_MAX_WATCHERS) {
                     watchers[pos++] = o->chan;
                  }
                  if (!start) {
                     start = o;
                  } else {
                     prev->call_next = o;
                  }
                  prev = o;
               }
            }
            numlines++;
         }
         if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
            (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) {
            break;
         }
         /* On "ringall" strategy we only move to the next penalty level
            when *all* ringing phones are done in the current penalty level */
         ring_one(qe, outgoing, &numbusies);
         /* and retry... */
      }
      if (pos == 1 /* not found */) {
         if (numlines == (numbusies + numnochan)) {
            ast_debug(1, "Everyone is busy at this time\n");
         } else {
            ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
         }
         *to = 0;
         return NULL;
      }

      /* Poll for events from both the incoming channel as well as any outgoing channels */
      winner = ast_waitfor_n(watchers, pos, to);

      /* Service all of the outgoing channels */
      for (o = start; o; o = o->call_next) {
         /* We go with a fixed buffer here instead of using ast_strdupa. Using
          * ast_strdupa in a loop like this one can cause a stack overflow
          */
         char ochan_name[AST_CHANNEL_NAME];

         if (o->chan) {
            ast_channel_lock(o->chan);
            ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
            ast_channel_unlock(o->chan);
         }
         if (o->stillgoing && (o->chan) &&  (ast_channel_state(o->chan) == AST_STATE_UP)) {
            if (!peer) {
               ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
               if (!o->block_connected_update) {
                  if (o->pending_connected_update) {
                     if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
                        ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
                        ast_channel_update_connected_line(in, &o->connected, NULL);
                     }
                  } else if (!o->dial_callerid_absent) {
                     ast_channel_lock(o->chan);
                     ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
                     ast_channel_unlock(o->chan);
                     connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
                     if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
                        ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
                        ast_channel_update_connected_line(in, &connected_caller, NULL);
                     }
                     ast_party_connected_line_free(&connected_caller);
                  }
               }
               if (o->aoc_s_rate_list) {
                  size_t encoded_size;
                  struct ast_aoc_encoded *encoded;
                  if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
                     ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
                     ast_aoc_destroy_encoded(encoded);
                  }
               }
               peer = o;
            }
         } else if (o->chan && (o->chan == winner)) {

            ast_copy_string(on, o->member->interface, sizeof(on));
            ast_copy_string(membername, o->member->membername, sizeof(membername));

            /* Before processing channel, go ahead and check for forwarding */
            if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
               ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
               numnochan++;
               do_hang(o);
               winner = NULL;
               continue;
            } else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
               struct ast_channel *original = o->chan;
               char tmpchan[256];
               char *stuff;
               char *tech;

               ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
               if ((stuff = strchr(tmpchan, '/'))) {
                  *stuff++ = '\0';
                  tech = tmpchan;
               } else {
                  snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), ast_channel_context(o->chan));
                  stuff = tmpchan;
                  tech = "Local";
               }
               if (!strcasecmp(tech, "Local")) {
                  /*
                   * Drop the connected line update block for local channels since
                   * this is going to run dialplan and the user can change his
                   * mind about what connected line information he wants to send.
                   */
                  o->block_connected_update = 0;
               }

               ast_cel_report_event(in, AST_CEL_FORWARD, NULL, ast_channel_call_forward(o->chan), NULL);

               ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
               /* Setup parameters */
               o->chan = ast_request(tech, ast_channel_nativeformats(in), in, stuff, &status);
               if (!o->chan) {
                  ast_log(LOG_NOTICE,
                     "Forwarding failed to create channel to dial '%s/%s'\n",
                     tech, stuff);
                  o->stillgoing = 0;
                  numnochan++;
               } else {
                  ast_channel_lock_both(o->chan, original);
                  ast_party_redirecting_copy(ast_channel_redirecting(o->chan),
                     ast_channel_redirecting(original));
                  ast_channel_unlock(o->chan);
                  ast_channel_unlock(original);

                  ast_channel_lock_both(o->chan, in);
                  ast_channel_inherit_variables(in, o->chan);
                  ast_channel_datastore_inherit(in, o->chan);

                  if (o->pending_connected_update) {
                     /*
                      * Re-seed the callattempt's connected line information with
                      * previously acquired connected line info from the queued
                      * channel.  The previously acquired connected line info could
                      * have been set through the CONNECTED_LINE dialplan function.
                      */
                     o->pending_connected_update = 0;
                     ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
                  }

                  ast_channel_accountcode_set(o->chan, ast_channel_accountcode(in));

                  if (!ast_channel_redirecting(o->chan)->from.number.valid
                     || ast_strlen_zero(ast_channel_redirecting(o->chan)->from.number.str)) {
                     /*
                      * The call was not previously redirected so it is
                      * now redirected from this number.
                      */
                     ast_party_number_free(&ast_channel_redirecting(o->chan)->from.number);
                     ast_party_number_init(&ast_channel_redirecting(o->chan)->from.number);
                     ast_channel_redirecting(o->chan)->from.number.valid = 1;
                     ast_channel_redirecting(o->chan)->from.number.str =
                        ast_strdup(S_OR(ast_channel_macroexten(in), ast_channel_exten(in)));
                  }

                  ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select;

                  o->dial_callerid_absent = !ast_channel_caller(o->chan)->id.number.valid
                     || ast_strlen_zero(ast_channel_caller(o->chan)->id.number.str);
                  ast_connected_line_copy_from_caller(ast_channel_connected(o->chan),
                     ast_channel_caller(in));

                  ast_channel_unlock(in);
                  if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
                     && !o->block_connected_update) {
                     struct ast_party_redirecting redirecting;

                     /*
                      * Redirecting updates to the caller make sense only on single
                      * call at a time strategies.
                      *
                      * We must unlock o->chan before calling
                      * ast_channel_redirecting_macro, because we put o->chan into
                      * autoservice there.  That is pretty much a guaranteed
                      * deadlock.  This is why the handling of o->chan's lock may
                      * seem a bit unusual here.
                      */
                     ast_party_redirecting_init(&redirecting);
                     ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(o->chan));
                     ast_channel_unlock(o->chan);
                     if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0) &&
                        ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
                        ast_channel_update_redirecting(in, &redirecting, NULL);
                     }
                     ast_party_redirecting_free(&redirecting);
                  } else {
                     ast_channel_unlock(o->chan);
                  }

                  if (ast_call(o->chan, stuff, 0)) {
                     ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
                        tech, stuff);
                     do_hang(o);
                     numnochan++;
                  }
               }
               /* Hangup the original channel now, in case we needed it */
               ast_hangup(winner);
               continue;
            }
            f = ast_read(winner);
            if (f) {
               if (f->frametype == AST_FRAME_CONTROL) {
                  switch (f->subclass.integer) {
                  case AST_CONTROL_ANSWER:
                     /* This is our guy if someone answered. */
                     if (!peer) {
                        ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
                        if (!o->block_connected_update) {
                           if (o->pending_connected_update) {
                              if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0) &&
                                 ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
                                 ast_channel_update_connected_line(in, &o->connected, NULL);
                              }
                           } else if (!o->dial_callerid_absent) {
                              ast_channel_lock(o->chan);
                              ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(o->chan));
                              ast_channel_unlock(o->chan);
                              connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
                              if (ast_channel_connected_line_sub(o->chan, in, &connected_caller, 0) &&
                                 ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
                                 ast_channel_update_connected_line(in, &connected_caller, NULL);
                              }
                              ast_party_connected_line_free(&connected_caller);
                           }
                        }
                        if (o->aoc_s_rate_list) {
                           size_t encoded_size;
                           struct ast_aoc_encoded *encoded;
                           if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
                              ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
                              ast_aoc_destroy_encoded(encoded);
                           }
                        }
                        peer = o;
                     }
                     break;
                  case AST_CONTROL_BUSY:
                     ast_verb(3, "%s is busy\n", ochan_name);
                     if (ast_channel_cdr(in)) {
                        ast_cdr_busy(ast_channel_cdr(in));
                     }
                     do_hang(o);
                     endtime = (long) time(NULL);
                     endtime -= starttime;
                     rna(endtime * 1000, qe, on, membername, qe->parent->autopausebusy);
                     if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
                        if (qe->parent->timeoutrestart) {
                           start_time_tv = ast_tvnow();
                        }
                        /* Have enough time for a queue member to answer? */
                        if (ast_remaining_ms(start_time_tv, orig) > 500) {
                           ring_one(qe, outgoing, &numbusies);
                           starttime = (long) time(NULL);
                        }
                     }
                     numbusies++;
                     break;
                  case AST_CONTROL_CONGESTION:
                     ast_verb(3, "%s is circuit-busy\n", ochan_name);
                     if (ast_channel_cdr(in)) {
                        ast_cdr_busy(ast_channel_cdr(in));
                     }
                     endtime = (long) time(NULL);
                     endtime -= starttime;
                     rna(endtime * 1000, qe, on, membername, qe->parent->autopauseunavail);
                     do_hang(o);
                     if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
                        if (qe->parent->timeoutrestart) {
                           start_time_tv = ast_tvnow();
                        }
                        if (ast_remaining_ms(start_time_tv, orig) > 500) {
                           ring_one(qe, outgoing, &numbusies);
                           starttime = (long) time(NULL);
                        }
                     }
                     numbusies++;
                     break;
                  case AST_CONTROL_RINGING:
                     ast_verb(3, "%s is ringing\n", ochan_name);

                     /* Start ring indication when the channel is ringing, if specified */
                     if (qe->ring_when_ringing) {
                        ast_moh_stop(qe->chan);
                        ast_indicate(qe->chan, AST_CONTROL_RINGING);
                     }
                     break;
                  case AST_CONTROL_OFFHOOK:
                     /* Ignore going off hook */
                     break;
                  case AST_CONTROL_CONNECTED_LINE:
                     if (o->block_connected_update) {
                        ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
                        break;
                     }
                     if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
                        struct ast_party_connected_line connected;

                        ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
                        ast_party_connected_line_set_init(&connected, &o->connected);
                        ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
                        ast_party_connected_line_set(&o->connected, &connected, NULL);
                        ast_party_connected_line_free(&connected);
                        o->pending_connected_update = 1;
                        break;
                     }

                     /*
                      * Prevent using the CallerID from the outgoing channel since we
                      * got a connected line update from it.
                      */
                     o->dial_callerid_absent = 1;

                     if (ast_channel_connected_line_sub(o->chan, in, f, 1) &&
                        ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
                        ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
                     }
                     break;
                  case AST_CONTROL_AOC:
                     {
                        struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
                        if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
                           ast_aoc_destroy_decoded(o->aoc_s_rate_list);
                           o->aoc_s_rate_list = decoded;
                        } else {
                           ast_aoc_destroy_decoded(decoded);
                        }
                     }
                     break;
                  case AST_CONTROL_REDIRECTING:
                     if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
                        /*
                         * Redirecting updates to the caller make sense only on single
                         * call at a time strategies.
                         */
                        break;
                     }
                     if (o->block_connected_update) {
                        ast_verb(3, "Redirecting update to %s prevented\n",
                           inchan_name);
                        break;
                     }
                     ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
                        ochan_name, inchan_name);
                     if (ast_channel_redirecting_sub(o->chan, in, f, 1) &&
                        ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
                        ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
                     }
                     break;
                  case AST_CONTROL_PVT_CAUSE_CODE:
                     ast_indicate_data(in, AST_CONTROL_PVT_CAUSE_CODE, f->data.ptr, f->datalen);
                     break;
                  default:
                     ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
                     break;
                  }
               }
               ast_frfree(f);
            } else { /* ast_read() returned NULL */
               endtime = (long) time(NULL) - starttime;
               rna(endtime * 1000, qe, on, membername, 1);
               do_hang(o);
               if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
                  if (qe->parent->timeoutrestart) {
                     start_time_tv = ast_tvnow();
                  }
                  if (ast_remaining_ms(start_time_tv, orig) > 500) {
                     ring_one(qe, outgoing, &numbusies);
                     starttime = (long) time(NULL);
                  }
               }
            }
         }
      }

      /* If we received an event from the caller, deal with it. */
      if (winner == in) {
         f = ast_read(in);
         if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
            /* Got hung up */
            *to = -1;
            if (f) {
               if (f->data.uint32) {
                  ast_channel_hangupcause_set(in, f->data.uint32);
               }
               ast_frfree(f);
            }
            return NULL;
         }

         if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
            ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
            *to = 0;
            ast_frfree(f);
            return NULL;
         }
         if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
            ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
            *to = 0;
            *digit = f->subclass.integer;
            ast_frfree(f);
            return NULL;
         }

         /* Send the frame from the in channel to all outgoing channels. */
         for (o = start; o; o = o->call_next) {
            if (!o->stillgoing || !o->chan) {
               /* This outgoing channel has died so don't send the frame to it. */
               continue;
            }
            switch (f->frametype) {
            case AST_FRAME_CONTROL:
               switch (f->subclass.integer) {
               case AST_CONTROL_CONNECTED_LINE:
                  if (ast_channel_connected_line_sub(in, o->chan, f, 1) &&
                     ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
                     ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
                  }
                  break;
               case AST_CONTROL_REDIRECTING:
                  if (ast_channel_redirecting_sub(in, o->chan, f, 1) &&
                     ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
                     ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
                  }
                  break;
               default:
                  /* We are not going to do anything with this frame. */
                  goto skip_frame;
               }
               break;
            default:
               /* We are not going to do anything with this frame. */
               goto skip_frame;
            }
         }
skip_frame:;

         ast_frfree(f);
      }
   }

   /* Make a position announcement, if enabled */
   if (qe->parent->announcefrequency && qe->parent->announce_to_first_user) {
      say_position(qe, ringing);
   }

   /* Make a periodic announcement, if enabled */
   if (qe->parent->periodicannouncefrequency && qe->parent->announce_to_first_user) {
      say_periodic_announcement(qe, ringing);
   }

   if (!*to) {
      for (o = start; o; o = o->call_next) {
         rna(orig, qe, o->interface, o->member->membername, 1);
      }
   }

#ifdef HAVE_EPOLL
   for (epollo = outgoing; epollo; epollo = epollo->q_next) {
      if (epollo->chan) {
         ast_poll_channel_del(in, epollo->chan);
      }
   }
#endif

   return peer;
}
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:
0if the caller's turn has arrived
-1if the caller should exit the queue.

Definition at line 4745 of file app_queue.c.

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

Referenced by queue_exec().

{
   int res = 0;

   /* This is the holding pen for callers 2 through maxlen */
   for (;;) {

      if (is_our_turn(qe)) {
         break;
      }

      /* If we have timed out, break out */
      if (qe->expire && (time(NULL) >= qe->expire)) {
         *reason = QUEUE_TIMEOUT;
         break;
      }

      if (qe->parent->leavewhenempty) {
         int status = 0;

         if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
            *reason = QUEUE_LEAVEEMPTY;
            ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
            leave_queue(qe);
            break;
         }
      }

      /* Make a position announcement, if enabled */
      if (qe->parent->announcefrequency &&
         (res = say_position(qe,ringing))) {
         break;
      }

      /* If we have timed out, break out */
      if (qe->expire && (time(NULL) >= qe->expire)) {
         *reason = QUEUE_TIMEOUT;
         break;
      }

      /* Make a periodic announcement, if enabled */
      if (qe->parent->periodicannouncefrequency &&
         (res = say_periodic_announcement(qe,ringing)))
         break;

      /* see if we need to move to the next penalty level for this queue */
      while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
         update_qe_rule(qe);
      }

      /* If we have timed out, break out */
      if (qe->expire && (time(NULL) >= qe->expire)) {
         *reason = QUEUE_TIMEOUT;
         break;
      }

      /* Wait a second before checking again */
      if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
         if (res > 0 && !valid_exit(qe, res)) {
            res = 0;
         } else {
            break;
         }
      }

      /* If we have timed out, break out */
      if (qe->expire && (time(NULL) >= qe->expire)) {
         *reason = QUEUE_TIMEOUT;
         break;
      }
   }

   return res;
}
static int word_in_list ( const char *  list,
const char *  word 
) [static]

Check if a given word is in a space-delimited list.

Parameters:
listSpace delimited list of words
wordThe word used to search the list
Note:
This function will not return 1 if the word is at the very end of the list (followed immediately by a \0, not a space) since it is used for checking tab-completion and a word at the end is still being tab-completed.
Returns:
Returns 1 if the word is found
Returns 0 if the word is not found

Definition at line 8404 of file app_queue.c.

Referenced by complete_queue().

                                                            {
   int list_len, word_len = strlen(word);
   const char *find, *end_find, *end_list;

   /* strip whitespace from front */
   while(isspace(*list)) {
      list++;
   }

   while((find = strstr(list, word))) {
      /* beginning of find starts inside another word? */
      if (find != list && *(find - 1) != ' ') {
         list = find;
         /* strip word from front */
         while(!isspace(*list) && *list != '\0') {
            list++;
         }
         /* strip whitespace from front */
         while(isspace(*list)) {
            list++;
         }
         continue;
      }

      /* end of find ends inside another word or at very end of list? */
      list_len = strlen(list);
      end_find = find + word_len;
      end_list = list + list_len;
      if (end_find == end_list || *end_find != ' ') {
         list = find;
         /* strip word from front */
         while(!isspace(*list) && *list != '\0') {
            list++;
         }
         /* strip whitespace from front */
         while(isspace(*list)) {
            list++;
         }
         continue;
      }

      /* terminating conditions satisfied, word at beginning or separated by ' ' */
      return 1;
   }

   return 0;
}

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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", } [static]

Definition at line 10041 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 1003 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 1005 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 1009 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 1013 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 1007 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 1011 of file app_queue.c.

Definition at line 10041 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 1025 of file app_queue.c.

Referenced by init_queue().

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

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

Definition at line 987 of file app_queue.c.

int log_membername_as_agent = 0 [static]

queues.conf [general] option

Definition at line 1043 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 1028 of file app_queue.c.

Referenced by init_queue().

int negative_penalty_invalid = 0 [static]

queues.conf [general] option

Definition at line 1040 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 1016 of file app_queue.c.

const char qpm_cmd_usage[] = "Usage: queue pause member <channel> in <queue> reason <reason>\n" [static]

Definition at line 9582 of file app_queue.c.

const char qsmp_cmd_usage[] = "Usage: queue set member penalty <channel> from <queue> <penalty>\n" [static]

Definition at line 9588 of file app_queue.c.

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

Definition at line 9870 of file app_queue.c.

Referenced by load_module().

struct ast_app_option queue_exec_options[128] = { [ 'C' ] = { .flag = OPT_MARK_AS_ANSWERED }, [ 'c' ] = { .flag = OPT_GO_ON }, [ 'd' ] = { .flag = OPT_DATA_QUALITY }, [ 'F' ] = { .flag = OPT_CALLEE_GO_ON , .arg_index = OPT_ARG_CALLEE_GO_ON + 1 }, [ 'h' ] = { .flag = OPT_CALLEE_HANGUP }, [ 'H' ] = { .flag = OPT_CALLER_HANGUP }, [ 'i' ] = { .flag = OPT_IGNORE_CALL_FW }, [ 'I' ] = { .flag = OPT_IGNORE_CONNECTEDLINE }, [ 'k' ] = { .flag = OPT_CALLEE_PARK }, [ 'K' ] = { .flag = OPT_CALLER_PARK }, [ 'n' ] = { .flag = OPT_NO_RETRY }, [ 'r' ] = { .flag = OPT_RINGING }, [ 'R' ] = { .flag = OPT_RING_WHEN_RINGING }, [ 't' ] = { .flag = OPT_CALLEE_TRANSFER }, [ 'T' ] = { .flag = OPT_CALLER_TRANSFER }, [ 'x' ] = { .flag = OPT_CALLEE_AUTOMIXMON }, [ 'X' ] = { .flag = OPT_CALLER_AUTOMIXMON }, [ 'w' ] = { .flag = OPT_CALLEE_AUTOMON }, [ 'W' ] = { .flag = OPT_CALLER_AUTOMON }, } [static]

Definition at line 936 of file app_queue.c.

Referenced by queue_exec().

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 1019 of file app_queue.c.

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

Referenced by set_queue_result().

Initial value:
 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}

a datastore used to help correctly log attended transfers of queue callers

Definition at line 5031 of file app_queue.c.

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

Initial value:
 {
   .name = "QUEUE_EXISTS",
   .read = queue_function_exists,
}

Definition at line 7709 of file app_queue.c.

Referenced by load_module(), and unload_module().

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

Definition at line 7725 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   .name = "QUEUE_MEMBER",
   .read = queue_function_mem_read,
   .write = queue_function_mem_write,
}

Definition at line 7719 of file app_queue.c.

Referenced by load_module(), and unload_module().

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

Definition at line 7735 of file app_queue.c.

Referenced by load_module(), and unload_module().

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

Definition at line 7740 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

Definition at line 9865 of file app_queue.c.

Initial value:
 {
   .name = "QUEUE_VARIABLES",
   .read = queue_function_var,
}

Definition at line 7714 of file app_queue.c.

Referenced by load_module(), and unload_module().

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

Definition at line 7730 of file app_queue.c.

Referenced by load_module(), and unload_module().

const char qum_cmd_usage[] = "Usage: queue unpause member <channel> in <queue> reason <reason>\n" [static]

Definition at line 9585 of file app_queue.c.

char* realtime_ringinuse_field [static]

name of the ringinuse field in the realtime database

Definition at line 1046 of file app_queue.c.

struct rule_lists rule_lists [static]
int shared_lastcall = 1 [static]

queues.conf [general] option

Definition at line 1031 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 1037 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 1022 of file app_queue.c.