Asterisk - The Open Source Telephony Project  21.4.1
Data Structures | Macros | Enumerations | Functions | Variables
app_queue.c File Reference

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

#include "asterisk.h"
#include <sys/time.h>
#include <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/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/aoc.h"
#include "asterisk/callerid.h"
#include "asterisk/term.h"
#include "asterisk/dial.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/stasis_message_router.h"
#include "asterisk/bridge_after.h"
#include "asterisk/stasis_bridges.h"
#include "asterisk/core_local.h"
#include "asterisk/mixmonitor.h"
#include "asterisk/bridge_basic.h"
#include "asterisk/max_forwards.h"

Go to the source code of this file.

Data Structures

struct  autopause
 
struct  call_queue
 
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
 
struct  local_optimization
 Structure representing relevant data during a local channel optimization. More...
 
struct  member
 
struct  penalty_rule
 
struct  queue_end_bridge
 
struct  queue_ent
 
struct  queue_stasis_data
 User data for stasis subscriptions used for queue calls. More...
 
struct  rule_list
 
struct  rule_lists
 
struct  strategy
 

Macros

#define ANNOUNCEHOLDTIME_ALWAYS   1
 
#define ANNOUNCEHOLDTIME_ONCE   2
 
#define ANNOUNCEPOSITION_LIMIT   4
 
#define ANNOUNCEPOSITION_MORE_THAN   3
 
#define ANNOUNCEPOSITION_NO   2
 
#define ANNOUNCEPOSITION_YES   1
 
#define AST_MAX_WATCHERS   256
 
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15
 The minimum number of seconds between position announcements. More...
 
#define DEFAULT_RETRY   5
 
#define DEFAULT_TIMEOUT   15
 
#define MAX_CALL_ATTEMPT_BUCKETS   353
 
#define MAX_PERIODIC_ANNOUNCEMENTS   10
 
#define MAX_QUEUE_BUCKETS   53
 
#define QUEUE_EVENT_VARIABLES   3
 
#define QUEUE_PAUSED_DEVSTATE   AST_DEVICE_INUSE
 
#define queue_ref(q)   ao2_bump(q)
 
#define queue_t_ref(q, tag)   ao2_t_bump(q, tag)
 
#define queue_t_unref(q, tag)   ({ ao2_t_cleanup(q, tag); NULL; })
 
#define QUEUE_UNKNOWN_PAUSED_DEVSTATE   AST_DEVICE_NOT_INUSE
 
#define QUEUE_UNPAUSED_DEVSTATE   AST_DEVICE_NOT_INUSE
 
#define queue_unref(q)   ({ ao2_cleanup(q); NULL; })
 
#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_CALLER   (-5)
 
#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), OPT_PREDIAL_CALLEE = (1 << 19),
  OPT_PREDIAL_CALLER = (1 << 20), OPT_MUSICONHOLD_CLASS = (1 << 21)
}
 
enum  {
  OPT_ARG_CALLEE_GO_ON = 0, OPT_ARG_PREDIAL_CALLEE, OPT_ARG_PREDIAL_CALLER, OPT_ARG_MUSICONHOLD_CLASS,
  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,
  QUEUE_WITHDRAW = 8
}
 
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. More...
 
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, const char *reason_paused, int wrapuptime)
 Add member to queue. More...
 
static struct call_queuealloc_queue (const char *queuename)
 
 AO2_STRING_FIELD_SORT_FN (call_queue, name)
 
static int aqm_exec (struct ast_channel *chan, const char *data)
 AddQueueMember application.
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
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. More...
 
static void callattempt_free (struct callattempt *doomed)
 
static int can_ring_entry (struct queue_ent *qe, struct callattempt *call)
 
static int change_priority_caller_on_queue (const char *queuename, const char *caller, int priority, int immediate)
 Change priority caller into a queue. More...
 
static void clear_queue (struct call_queue *q)
 
static int clear_stats (const char *queuename)
 Facilitates resetting statistics for a queue. More...
 
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. More...
 
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 int context_included (const char *parent, const char *child)
 Returns if one context includes another context. More...
 
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, int wrapuptime)
 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 destroy_queue_member_cb (void *obj)
 
static void device_state_cb (void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 set a member's status based on device state of that member's interface
 
static void do_hang (struct callattempt *o)
 common hangup actions
 
static void do_print (struct mansession *s, int fd, const char *str)
 direct output 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. More...
 
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 void escape_and_substitute (struct ast_channel *chan, const char *input, char *output, size_t size)
 
static int extension_state_cb (const char *context, const 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)
 Find a member by looking up queuename and interface. More...
 
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. More...
 
static void free_members (struct call_queue *q, int all)
 Iterate through queue's member list and delete them.
 
static struct memberget_interface_helper (struct call_queue *q, const char *interface)
 
static int get_member_penalty (char *queuename, char *interface)
 Gets members penalty. More...
 
static int get_member_status (struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
 Check if members are available. More...
 
static int get_queue_member_status (struct member *cur)
 Return the current state of a member.
 
static int get_wrapuptime (struct call_queue *q, struct member *member)
 Return wrapuptime. More...
 
static void handle_attended_transfer (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 Handle an attended transfer event. More...
 
static void handle_blind_transfer (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 Handle a blind transfer event. More...
 
static void handle_bridge_enter (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static void handle_hangup (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static void handle_local_optimization_begin (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static void handle_local_optimization_end (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static void handle_masquerade (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static char * handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static char * handle_queue_change_priority_caller (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 void hangupcalls (struct queue_ent *qe, 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. More...
 
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. More...
 
static const char * int2strat (int strategy)
 
static struct memberinterface_exists (struct call_queue *q, const char *interface)
 
static int is_longest_waiting_caller (struct queue_ent *caller, struct member *member)
 
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. More...
 
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_if_unfound (void *obj, void *arg, int flags)
 
static void leave_queue (struct queue_ent *qe)
 Caller leaving queue. More...
 
static int load_module (void)
 Load the module. More...
 
static void load_realtime_queues (const char *queuename)
 
static int load_realtime_rules (void)
 Load queue rules from realtime. More...
 
static void log_attended_transfer (struct queue_stasis_data *queue_data, struct ast_attended_transfer_message *atxfer_msg)
 
static int manager_add_queue_member (struct mansession *s, const struct message *m)
 
static int manager_change_priority_caller_on_queue (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_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 manager_request_withdraw_caller_from_queue (struct mansession *s, const struct message *m)
 
static int mark_member_dead (void *obj, void *arg, int flags)
 
static int mark_unfound (void *obj, void *arg, int flags)
 
static void member_add_to_queue (struct call_queue *queue, 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. More...
 
static void parse_empty_options (const char *value, enum empty_conditions *empty, int joinempty)
 
static int pending_members_cmp (void *obj, void *arg, int flags)
 
static int pending_members_hash (const void *obj, const int flags)
 
static void pending_members_remove (struct member *mem)
 
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 void print_queue (struct mansession *s, int fd, struct call_queue *q)
 Print a single queue to AMI or the CLI.
 
static void publish_dial_end_event (struct ast_channel *in, struct callattempt *outgoing, struct ast_channel *exception, const char *status)
 
static int publish_queue_member_pause (struct call_queue *q, struct member *member)
 
static int ql_exec (struct ast_channel *chan, const char *data)
 QueueLog application.
 
static struct ast_manager_event_blobqueue_agent_called_to_ami (struct stasis_message *message)
 
static void queue_agent_cb (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static struct ast_manager_event_blobqueue_agent_complete_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_agent_connect_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_agent_dump_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_agent_ringnoanswer_to_ami (struct stasis_message *message)
 
static void queue_bridge_cb (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static struct ast_manager_event_blobqueue_caller_abandon_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_caller_join_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_caller_leave_to_ami (struct stasis_message *message)
 
static void queue_channel_cb (void *userdata, struct stasis_subscription *sub, struct stasis_message *msg)
 
static struct ast_manager_event_blobqueue_channel_to_ami (const char *type, struct stasis_message *message)
 
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. More...
 
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. More...
 
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) More...
 
static int queue_function_queuegetchannel (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue.
 
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. More...
 
static int queue_hash_cb (const void *obj, const int flags)
 
static struct ast_manager_event_blobqueue_member_added_to_ami (struct stasis_message *message)
 
static struct ast_jsonqueue_member_blob_create (struct call_queue *q, struct member *mem)
 
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 ast_manager_event_blobqueue_member_pause_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_member_penalty_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_member_removed_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_member_ringinuse_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_member_status_to_ami (struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_member_to_ami (const char *type, struct stasis_message *message)
 
static struct ast_manager_event_blobqueue_multi_channel_to_ami (const char *type, struct stasis_message *message)
 
static void queue_publish_member_blob (struct stasis_message_type *type, struct ast_json *blob)
 
static void queue_publish_multi_channel_blob (struct ast_channel *caller, struct ast_channel *agent, struct stasis_message_type *type, struct ast_json *blob)
 
static void queue_publish_multi_channel_snapshot_blob (struct stasis_topic *topic, struct ast_channel_snapshot *caller_snapshot, struct ast_channel_snapshot *agent_snapshot, struct stasis_message_type *type, struct ast_json *blob)
 
static void queue_reset_global_params (void)
 
static void queue_rules_reset_global_params (void)
 
static void queue_rules_set_global_params (struct ast_config *cfg)
 
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. More...
 
static char * queue_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 
static struct queue_stasis_dataqueue_stasis_data_alloc (struct queue_ent *qe, struct ast_channel *peer, struct member *mem, time_t holdstart, time_t starttime, int callcompletedinsl)
 
static void queue_stasis_data_destructor (void *obj)
 
static int qupd_exec (struct ast_channel *chan, const char *data)
 Update Queue with data of an outgoing call.
 
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. More...
 
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. More...
 
static int reload_queues (int reload, struct ast_flags *mask, const char *queuename)
 reload the queues.conf file More...
 
static void reload_single_member (const char *memberdata, struct call_queue *q)
 reload information pertaining to a single member More...
 
static void reload_single_queue (struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
 Reload information pertaining to a particular queue. More...
 
static int remove_from_queue (const char *queuename, const char *interface)
 Remove member from queue. More...
 
static void remove_stasis_subscriptions (struct queue_stasis_data *queue_data)
 
static int request_withdraw_caller_from_queue (const char *queuename, const char *caller, const char *withdraw_info)
 Request to withdraw a caller from a queue. More...
 
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
 Part 2 of ring_one. More...
 
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member. More...
 
static void rna (int rnatime, struct queue_ent *qe, struct ast_channel *peer, 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 *category, struct ast_config *member_config)
 Find rt member record to update otherwise create one. More...
 
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 char *queuename, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart, time_t callstart, 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_member_pause (struct call_queue *q, struct member *mem, const char *reason, int paused)
 
static void set_queue_member_ringinuse (struct call_queue *q, struct member *mem, int ringinuse)
 
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 void setup_mixmonitor (struct queue_ent *qe, const char *filename)
 
static void setup_peer_after_bridge_goto (struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
 
static int setup_stasis_subs (struct queue_ent *qe, struct ast_channel *peer, struct member *mem, time_t holdstart, time_t starttime, int callcompletedinsl)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_caller_join_type,.to_ami=queue_caller_join_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_caller_leave_type,.to_ami=queue_caller_leave_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_caller_abandon_type,.to_ami=queue_caller_abandon_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_member_status_type,.to_ami=queue_member_status_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_member_added_type,.to_ami=queue_member_added_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_member_removed_type,.to_ami=queue_member_removed_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_member_pause_type,.to_ami=queue_member_pause_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_member_penalty_type,.to_ami=queue_member_penalty_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_member_ringinuse_type,.to_ami=queue_member_ringinuse_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_agent_called_type,.to_ami=queue_agent_called_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_agent_connect_type,.to_ami=queue_agent_connect_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_agent_complete_type,.to_ami=queue_agent_complete_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_agent_dump_type,.to_ami=queue_agent_dump_to_ami,)
 
 STASIS_MESSAGE_TYPE_DEFN_LOCAL (queue_agent_ringnoanswer_type,.to_ami=queue_agent_ringnoanswer_to_ami,)
 
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, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *gosub, int ringing)
 
static int unload_module (void)
 
static void update_connected_line_from_peer (struct ast_channel *chan, struct ast_channel *peer, int is_caller)
 
static void update_qe_rule (struct queue_ent *qe)
 update rules for queues More...
 
static int update_queue (struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
 update the queue status More...
 
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 void 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. More...
 
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. More...
 
static int wait_a_bit (struct queue_ent *qe)
 
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 Wait for a member to answer the call. More...
 
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. More...
 
static int word_in_list (const char *list, const char *word)
 Check if a given word is in a space-delimited list. More...
 

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 = "da6642af068ee5e6490c5b1d2cc1d238" , .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, }
 
static struct stasis_message_routeragent_router
 
static char * app = "Queue"
 
static char * app_aqm = "AddQueueMember"
 
static char * app_pqm = "PauseQueueMember"
 
static char * app_ql = "QueueLog"
 
static char * app_qupd = "QueueUpdate"
 
static char * app_rqm = "RemoveQueueMember"
 
static char * app_upqm = "UnpauseQueueMember"
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static int autofill_default
 queues.conf [general] option
 
static const struct autopause autopausesmodes []
 
static struct ast_cli_entry cli_queue []
 
static struct stasis_subscriptiondevice_state_sub
 Subscription to device state change messages.
 
static int force_longest_waiting_caller
 queues.conf [general] option
 
static int log_membername_as_agent
 queues.conf [general] option
 
static int montype_default
 queues.conf [general] option
 
static int negative_penalty_invalid
 queues.conf [general] option
 
static struct ao2_containerpending_members
 
static const char *const pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
 
static const struct ast_app_option queue_exec_options [128] = { [ 'b' ] = { .flag = OPT_PREDIAL_CALLEE , .arg_index = OPT_ARG_PREDIAL_CALLEE + 1 }, [ 'B' ] = { .flag = OPT_PREDIAL_CALLER , .arg_index = OPT_ARG_PREDIAL_CALLER + 1 }, [ '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 }, [ 'm' ] = { .flag = OPT_MUSICONHOLD_CLASS , .arg_index = OPT_ARG_MUSICONHOLD_CLASS + 1 }, [ '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
 queues.conf [general] option
 
struct {
   enum queue_result   id
 
   char *   text
 
queue_results []
 
static struct ast_custom_function queueexists_function
 
static struct ast_custom_function queuegetchannel_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_custom_function queuevar_function
 
static struct ast_custom_function queuewaitingcount_function
 
static int realtime_reason_paused
 does realtime backend support reason_paused
 
static char * realtime_ringinuse_field
 name of the ringinuse field in the realtime database
 
static int realtime_rules
 queuerules.conf [general] option
 
static struct rule_lists rule_lists = { .first = NULL, .last = NULL, .lock = { PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP , NULL, {1, 0} } , }
 
static int shared_lastcall
 queues.conf [general] option
 
static const struct strategy strategies []
 
static struct stasis_forwardtopic_forwarder
 
static int use_weight
 Records that one or more queues use weight.
 

Detailed Description

True call queues with optional send URL on answer.

Author
Mark Spencer marks.nosp@m.ter@.nosp@m.digiu.nosp@m.m.co.nosp@m.m
Development notes
Note
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay kevin.nosp@m.l@ne.nosp@m.tnati.nosp@m.on.c.nosp@m.om

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@.nosp@m.toad.nosp@m..net:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter

Patch Version 1.07 2003-12-24 01

Added servicelevel statistic by Michiel Betel michi.nosp@m.el@b.nosp@m.etel..nosp@m.nl Added Priority jumping code for adding and removing queue members by Jonathan Stanton aster.nosp@m.isk@.nosp@m.doilo.nosp@m.okli.nosp@m.keica.nosp@m.re.c.nosp@m.om

Fixed to work with CVS as of 2004-02-25 and released as 1.07a by Matthew Enger m.eng.nosp@m.er@x.nosp@m.i.com.nosp@m..au

Definition in file app_queue.c.

Macro Definition Documentation

#define ANNOUNCEPOSITION_LIMIT   4

We not announce position more than <limit>

Definition at line 1789 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_MORE_THAN   3

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

Definition at line 1788 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 1787 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 1786 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15

The minimum number of seconds between position announcements.

Note
The default value of 15 provides backwards compatibility.

Definition at line 1561 of file app_queue.c.

Referenced by init_queue().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

The maximum periodic announcements we can have

Definition at line 1556 of file app_queue.c.

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

#define RECHECK   1

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

Definition at line 1555 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 1566 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), remove_from_queue(), request_withdraw_caller_from_queue(), and rqm_exec().

#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_CALLER   (-5)

Caller not found

Definition at line 1570 of file app_queue.c.

Referenced by change_priority_caller_on_queue(), and request_withdraw_caller_from_queue().

#define RES_NOT_DYNAMIC   (-4)

Member is not dynamic

Definition at line 1569 of file app_queue.c.

Referenced by remove_from_queue(), and rqm_exec().

#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Out of memory

Definition at line 1567 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), and reload_queue_members().

Enumeration Type Documentation

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

Definition at line 1446 of file app_queue.c.

1446  {
1447  OPT_MARK_AS_ANSWERED = (1 << 0),
1448  OPT_GO_ON = (1 << 1),
1449  OPT_DATA_QUALITY = (1 << 2),
1450  OPT_CALLEE_GO_ON = (1 << 3),
1451  OPT_CALLEE_HANGUP = (1 << 4),
1452  OPT_CALLER_HANGUP = (1 << 5),
1453  OPT_IGNORE_CALL_FW = (1 << 6),
1454  OPT_IGNORE_CONNECTEDLINE = (1 << 7),
1455  OPT_CALLEE_PARK = (1 << 8),
1456  OPT_CALLER_PARK = (1 << 9),
1457  OPT_NO_RETRY = (1 << 10),
1458  OPT_RINGING = (1 << 11),
1459  OPT_RING_WHEN_RINGING = (1 << 12),
1460  OPT_CALLEE_TRANSFER = (1 << 13),
1461  OPT_CALLER_TRANSFER = (1 << 14),
1462  OPT_CALLEE_AUTOMIXMON = (1 << 15),
1463  OPT_CALLER_AUTOMIXMON = (1 << 16),
1464  OPT_CALLEE_AUTOMON = (1 << 17),
1465  OPT_CALLER_AUTOMON = (1 << 18),
1466  OPT_PREDIAL_CALLEE = (1 << 19),
1467  OPT_PREDIAL_CALLER = (1 << 20),
1468  OPT_MUSICONHOLD_CLASS = (1 << 21),
1469 };

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

References AO2_ALLOC_OPT_LOCK_NOLOCK, ao2_container_alloc_rbtree, ao2_container_dup(), ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), AO2_ITERATOR_UNLINK, ao2_ref, ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_load_realtime_multientry(), ast_str_buffer(), ast_str_set(), do_print(), find_load_queue_rt_friendly(), call_queue::name, and print_queue().

10139 {
10140  struct call_queue *q;
10141  struct ast_str *out = ast_str_alloca(512);
10142  struct ao2_container *sorted_queues;
10143 
10144  struct ao2_iterator queue_iter;
10145  int found = 0;
10146 
10147  if (argc != 2 && argc != 3) {
10148  return CLI_SHOWUSAGE;
10149  }
10150 
10151  if (argc == 3) { /* specific queue */
10152  if ((q = find_load_queue_rt_friendly(argv[2]))) {
10153  ao2_lock(q);
10154  print_queue(s, fd, q);
10155  ao2_unlock(q);
10156  queue_unref(q);
10157  } else {
10158  ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
10159  do_print(s, fd, ast_str_buffer(out));
10160  }
10161  return CLI_SUCCESS;
10162  }
10163 
10164  if (ast_check_realtime("queues")) {
10165  /* This block is to find any queues which are defined in realtime but
10166  * which have not yet been added to the in-core container
10167  */
10168  struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
10169  if (cfg) {
10170  char *category = NULL;
10171  while ((category = ast_category_browse(cfg, category))) {
10172  const char *queuename = ast_variable_retrieve(cfg, category, "name");
10173  if (ast_strlen_zero(queuename)) {
10174  ast_log(LOG_WARNING, "Ignoring realtime queue with a NULL or empty 'name.'\n");
10175  continue;
10176  }
10177  if ((q = find_load_queue_rt_friendly(queuename))) {
10178  queue_t_unref(q, "Done with temporary pointer");
10179  }
10180  }
10181  ast_config_destroy(cfg);
10182  }
10183  }
10184 
10185  /*
10186  * Snapping a copy of the container prevents having to lock both the queues container
10187  * and the queue itself at the same time. It also allows us to sort the entries.
10188  */
10189  sorted_queues = ao2_container_alloc_rbtree(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, call_queue_sort_fn, NULL);
10190  if (!sorted_queues) {
10191  return CLI_SUCCESS;
10192  }
10193  if (ao2_container_dup(sorted_queues, queues, 0)) {
10194  ao2_ref(sorted_queues, -1);
10195  return CLI_SUCCESS;
10196  }
10197 
10198  /*
10199  * No need to lock the container since it's temporary and static.
10200  * We also unlink the entries as we use them so the container is
10201  * empty when the iterator finishes. We can then just unref the container.
10202  */
10203  queue_iter = ao2_iterator_init(sorted_queues, AO2_ITERATOR_DONTLOCK | AO2_ITERATOR_UNLINK);
10204  while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10205  struct call_queue *realtime_queue = NULL;
10206  ao2_lock(q);
10207  /* This check is to make sure we don't print information for realtime
10208  * queues which have been deleted from realtime but which have not yet
10209  * been deleted from the in-core container. Only do this if we're not
10210  * looking for a specific queue.
10211  */
10212  if (q->realtime) {
10213  realtime_queue = find_load_queue_rt_friendly(q->name);
10214  if (!realtime_queue) {
10215  ao2_unlock(q);
10216  queue_t_unref(q, "Done with iterator");
10217  continue;
10218  }
10219  queue_t_unref(realtime_queue, "Queue is already in memory");
10220  }
10221 
10222  found = 1;
10223  print_queue(s, fd, q);
10224 
10225  ao2_unlock(q);
10226  queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
10227  }
10228  ao2_iterator_destroy(&queue_iter);
10229  ao2_ref(sorted_queues, -1);
10230  if (!found) {
10231  ast_str_set(&out, 0, "No queues.");
10232  do_print(s, fd, ast_str_buffer(out));
10233  }
10234  return CLI_SUCCESS;
10235 }
static void do_print(struct mansession *s, int fd, const char *str)
direct output to manager or cli with proper terminator
Definition: app_queue.c:10020
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3530
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
Support for dynamic strings.
Definition: strings.h:623
int ao2_container_dup(struct ao2_container *dest, struct ao2_container *src, enum search_flags flags)
Copy all object references in the src container into the dest container.
const ast_string_field name
Definition: app_queue.c:1829
Assume that the ao2_container is already locked.
Definition: astobj2.h:1852
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
static void print_queue(struct mansession *s, int fd, struct call_queue *q)
Print a single queue to AMI or the CLI.
Definition: app_queue.c:10030
#define ao2_container_alloc_rbtree(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a red-black tree container.
Definition: astobj2.h:1349
Generic container type.
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static int add_to_queue ( const char *  queuename,
const char *  interface,
const char *  membername,
int  penalty,
int  paused,
int  dump,
const char *  state_interface,
const char *  reason_paused,
int  wrapuptime 
)
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 7593 of file app_queue.c.

References ao2_ref, ast_copy_string(), AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), create_queue_member(), dump_queue_members(), member::dynamic, find_load_queue_rt_friendly(), call_queue::name, member::reason_paused, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by aqm_exec(), and reload_queue_members().

7594 {
7595  struct call_queue *q;
7596  struct member *new_member, *old_member;
7597  int res = RES_NOSUCHQUEUE;
7598 
7599  /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7600  * short-circuits if the queue is already in memory. */
7601  if (!(q = find_load_queue_rt_friendly(queuename))) {
7602  return res;
7603  }
7604 
7605  ao2_lock(q);
7606  if ((old_member = interface_exists(q, interface)) == NULL) {
7607  if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface, q->ringinuse, wrapuptime))) {
7608  new_member->dynamic = 1;
7609  if (reason_paused) {
7610  ast_copy_string(new_member->reason_paused, reason_paused, sizeof(new_member->reason_paused));
7611  }
7612  member_add_to_queue(q, new_member);
7613  queue_publish_member_blob(queue_member_added_type(), queue_member_blob_create(q, new_member));
7614 
7615  if (is_member_available(q, new_member)) {
7617  }
7618 
7619  ao2_ref(new_member, -1);
7620  new_member = NULL;
7621 
7622  if (dump) {
7623  dump_queue_members(q);
7624  }
7625 
7626  res = RES_OKAY;
7627  } else {
7628  res = RES_OUTOFMEMORY;
7629  }
7630  } else {
7631  ao2_ref(old_member, -1);
7632  res = RES_EXISTS;
7633  }
7634  ao2_unlock(q);
7635  queue_t_unref(q, "Expiring temporary reference");
7636 
7637  return res;
7638 }
int dynamic
Definition: app_queue.c:1735
static struct member * create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
allocate space for new queue member and set fields based on parameters passed
Definition: app_queue.c:2865
int paused
Definition: app_queue.c:1738
#define RES_OKAY
Definition: app_queue.c:1565
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1730
int wrapuptime
Definition: app_queue.c:1742
char membername[80]
Definition: app_queue.c:1732
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
#define RES_OUTOFMEMORY
Definition: app_queue.c:1567
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
int penalty
Definition: app_queue.c:1733
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
const ast_string_field name
Definition: app_queue.c:1829
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
Definition: app_queue.c:7485
char reason_paused[80]
Definition: app_queue.c:1739
#define RES_NOSUCHQUEUE
Definition: app_queue.c:1568
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define RES_EXISTS
Definition: app_queue.c:1566
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 6044 of file app_queue.c.

References ao2_container_count(), ast_debug, member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, queue_ent::max_penalty, call_queue::members, queue_ent::min_penalty, member::penalty, call_queue::penaltymemberslimit, queue_ent::pos, member::queuepos, queue_ent::raise_penalty, and call_queue::rrpos.

6045 {
6046  /* disregarding penalty on too few members? */
6047  int membercount = ao2_container_count(q->members);
6048  unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
6049  int penalty = mem->penalty;
6050 
6051  if (usepenalty) {
6052  if (qe->raise_penalty != INT_MAX && penalty < qe->raise_penalty) {
6053  /* Low penalty is raised up to the current minimum */
6054  penalty = qe->raise_penalty;
6055  }
6056  if ((qe->max_penalty != INT_MAX && penalty > qe->max_penalty) ||
6057  (qe->min_penalty != INT_MAX && penalty < qe->min_penalty)) {
6058  return -1;
6059  }
6060  } else {
6061  ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
6062  membercount, q->penaltymemberslimit);
6063  }
6064 
6065  switch (q->strategy) {
6066  case QUEUE_STRATEGY_RINGALL:
6067  /* Everyone equal, except for penalty */
6068  tmp->metric = penalty * 1000000 * usepenalty;
6069  break;
6070  case QUEUE_STRATEGY_LINEAR:
6071  if (pos < qe->linpos) {
6072  tmp->metric = 1000 + pos;
6073  } else {
6074  if (pos > qe->linpos) {
6075  /* Indicate there is another priority */
6076  qe->linwrapped = 1;
6077  }
6078  tmp->metric = pos;
6079  }
6080  tmp->metric += penalty * 1000000 * usepenalty;
6081  break;
6082  case QUEUE_STRATEGY_RRORDERED:
6083  case QUEUE_STRATEGY_RRMEMORY:
6084  pos = mem->queuepos;
6085  if (pos < q->rrpos) {
6086  tmp->metric = 1000 + pos;
6087  } else {
6088  if (pos > q->rrpos) {
6089  /* Indicate there is another priority */
6090  q->wrapped = 1;
6091  }
6092  tmp->metric = pos;
6093  }
6094  tmp->metric += penalty * 1000000 * usepenalty;
6095  break;
6096  case QUEUE_STRATEGY_RANDOM:
6097  tmp->metric = ast_random() % 1000;
6098  tmp->metric += penalty * 1000000 * usepenalty;
6099  break;
6100  case QUEUE_STRATEGY_WRANDOM:
6101  tmp->metric = ast_random() % ((1 + penalty) * 1000);
6102  break;
6103  case QUEUE_STRATEGY_FEWESTCALLS:
6104  tmp->metric = mem->calls;
6105  tmp->metric += penalty * 1000000 * usepenalty;
6106  break;
6107  case QUEUE_STRATEGY_LEASTRECENT:
6108  if (!mem->lastcall) {
6109  tmp->metric = 0;
6110  } else {
6111  tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
6112  }
6113  tmp->metric += penalty * 1000000 * usepenalty;
6114  break;
6115  default:
6116  ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
6117  break;
6118  }
6119  return 0;
6120 }
int queuepos
Definition: app_queue.c:1740
int raise_penalty
Definition: app_queue.c:1712
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
int min_penalty
Definition: app_queue.c:1711
int max_penalty
Definition: app_queue.c:1710
int penaltymemberslimit
Definition: app_queue.c:1871
int linwrapped
Definition: app_queue.c:1714
struct ao2_container * members
Definition: app_queue.c:1887
int penalty
Definition: app_queue.c:1733
#define ast_debug(level,...)
Log a DEBUG message.
int linpos
Definition: app_queue.c:1713
int calls
Definition: app_queue.c:1734
time_t lastcall
Definition: app_queue.c:1744
static int change_priority_caller_on_queue ( const char *  queuename,
const char *  caller,
int  priority,
int  immediate 
)
static

Change priority caller into a queue.

Return values
RES_NOSUCHQUEUEqueue does not exist
RES_OKAYchange priority
RES_NOT_CALLERqueue exists but no caller
Note
Ensure the appropriate realtime queue is loaded. Note that this short-circuits if the queue is already in memory.

Definition at line 7646 of file app_queue.c.

References ast_debug, queue_ent::chan, find_load_queue_rt_friendly(), call_queue::head, insert_entry(), queue_ent::next, queue_ent::pos, queue_ent::prio, RES_NOSUCHQUEUE, RES_NOT_CALLER, and RES_OKAY.

7647 {
7648  struct call_queue *q;
7649  struct queue_ent *current, *prev = NULL, *caller_qe = NULL;
7650  int res = RES_NOSUCHQUEUE;
7651 
7652  /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7653  * short-circuits if the queue is already in memory. */
7654  if (!(q = find_load_queue_rt_friendly(queuename))) {
7655  return res;
7656  }
7657 
7658  ao2_lock(q);
7659  res = RES_NOT_CALLER;
7660  for (current = q->head; current; current = current->next) {
7661  if (strcmp(ast_channel_name(current->chan), caller) == 0) {
7662  ast_debug(1, "%s Caller new priority %d in queue %s\n",
7663  caller, priority, queuename);
7664  current->prio = priority;
7665  if (immediate) {
7666  /* This caller is being immediately moved in the queue so remove them */
7667  if (prev) {
7668  prev->next = current->next;
7669  } else {
7670  q->head = current->next;
7671  }
7672  caller_qe = current;
7673  /* The position for all callers is not recalculated in here as it will
7674  * be updated when the moved caller is inserted back into the queue
7675  */
7676  }
7677  res = RES_OKAY;
7678  break;
7679  } else if (immediate) {
7680  prev = current;
7681  }
7682  }
7683 
7684  if (caller_qe) {
7685  int inserted = 0, pos = 0;
7686 
7687  /* If a caller queue entry exists, we are applying their priority immediately
7688  * and have to reinsert them at the correct position.
7689  */
7690  prev = NULL;
7691  current = q->head;
7692  while (current) {
7693  if (!inserted && (caller_qe->prio > current->prio)) {
7694  insert_entry(q, prev, caller_qe, &pos);
7695  inserted = 1;
7696  }
7697 
7698  /* We always update the position as it may have changed */
7699  current->pos = ++pos;
7700 
7701  /* Move to the next caller in the queue */
7702  prev = current;
7703  current = current->next;
7704  }
7705 
7706  if (!inserted) {
7707  insert_entry(q, prev, caller_qe, &pos);
7708  }
7709  }
7710 
7711  ao2_unlock(q);
7712  return res;
7713 }
#define RES_OKAY
Definition: app_queue.c:1565
struct ast_channel * chan
Definition: app_queue.c:1720
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
struct queue_ent * head
Definition: app_queue.c:1888
#define RES_NOT_CALLER
Definition: app_queue.c:1570
#define ast_debug(level,...)
Log a DEBUG message.
#define RES_NOSUCHQUEUE
Definition: app_queue.c:1568
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'.
Definition: app_queue.c:2089
struct queue_ent * next
Definition: app_queue.c:1723
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
0always

Definition at line 9973 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), and call_queue::name.

Referenced by reload_handler().

9974 {
9975  struct call_queue *q;
9976  struct ao2_iterator queue_iter;
9977 
9978  queue_iter = ao2_iterator_init(queues, 0);
9979  while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
9980  ao2_lock(q);
9981  if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
9982  clear_queue(q);
9983  ao2_unlock(q);
9984  queue_t_unref(q, "Done with iterator");
9985  }
9986  ao2_iterator_destroy(&queue_iter);
9987  return 0;
9988 }
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
const ast_string_field name
Definition: app_queue.c:1829
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
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 10309 of file app_queue.c.

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

10310 {
10311  struct call_queue *q;
10312  char *ret = NULL;
10313  int which = 0;
10314  int wordlen = strlen(word);
10315  struct ao2_iterator queue_iter;
10316  const char *word_list = NULL;
10317 
10318  /* for certain commands, already completed items should be left out of
10319  * the list */
10320  if (word_list_offset && strlen(line) >= word_list_offset) {
10321  word_list = line + word_list_offset;
10322  }
10323 
10324  queue_iter = ao2_iterator_init(queues, 0);
10325  while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
10326  if (!strncasecmp(word, q->name, wordlen) && ++which > state
10327  && (!word_list_offset || !word_in_list(word_list, q->name))) {
10328  ret = ast_strdup(q->name);
10329  queue_t_unref(q, "Done with iterator");
10330  break;
10331  }
10332  queue_t_unref(q, "Done with iterator");
10333  }
10334  ao2_iterator_destroy(&queue_iter);
10335 
10336  /* Pretend "rules" is at the end of the queues list in certain
10337  * circumstances since it is an alternate command that should be
10338  * tab-completable for "queue show" */
10339  if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
10340  ret = ast_strdup("rules");
10341  }
10342 
10343  return ret;
10344 }
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
static int word_in_list(const char *list, const char *word)
Check if a given word is in a space-delimited list.
Definition: app_queue.c:10250
const ast_string_field name
Definition: app_queue.c:1829
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static int context_included ( const char *  parent,
const char *  child 
)
static

Returns if one context includes another context.

Parameters
parentParent context to search for child
childContext to check for inclusion in parent

This function recursively checks if the context child is included in the context parent.

Return values
1if child is included in parent
0if not

Definition at line 2777 of file app_queue.c.

References ast_context_find().

2778 {
2779  struct ast_context *c = NULL;
2780 
2781  c = ast_context_find(parent);
2782  if (!c) {
2783  /* well, if parent doesn't exist, how can the child be included in it? */
2784  return 0;
2785  }
2786  if (!strcmp(ast_get_context_name(c), parent)) {
2787  /* found the context of the hint app_queue is using. Now, see
2788  if that context includes the one that just changed state */
2789  struct ast_include *inc = NULL;
2790 
2791  while ((inc = (struct ast_include*) ast_walk_context_includes(c, inc))) {
2792  const char *includename = ast_get_include_name(inc);
2793  if (!strcasecmp(child, includename)) {
2794  return 1;
2795  }
2796  /* recurse on this context, for nested includes. The
2797  PBX extension parser will prevent infinite recursion. */
2798  if (context_included(includename, child)) {
2799  return 1;
2800  }
2801  }
2802  }
2803  return 0;
2804 }
ast_include: include= support in extensions.conf
Definition: pbx_include.c:37
struct ast_context * ast_context_find(const char *name)
Find a context.
Definition: extconf.c:4172
ast_context: An extension context
Definition: pbx.c:284
static int context_included(const char *parent, const char *child)
Returns if one context includes another context.
Definition: app_queue.c:2777
static void dump_queue_members ( struct call_queue pm_queue)
static

Dump all members in a specific queue to the database.

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

Definition at line 7485 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ast_db_del(), ast_db_put(), ast_str_append(), ast_str_buffer(), ast_str_create, ast_str_strlen(), member::dynamic, member::interface, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::reason_paused, member::state_interface, and member::wrapuptime.

Referenced by add_to_queue(), and remove_from_queue().

7486 {
7487  struct member *cur_member;
7488  struct ast_str *value;
7489  struct ao2_iterator mem_iter;
7490 
7491  if (!pm_queue) {
7492  return;
7493  }
7494 
7495  /* 4K is a reasonable default for most applications, but we grow to
7496  * accommodate more if necessary. */
7497  if (!(value = ast_str_create(4096))) {
7498  return;
7499  }
7500 
7501  mem_iter = ao2_iterator_init(pm_queue->members, 0);
7502  while ((cur_member = ao2_iterator_next(&mem_iter))) {
7503  if (!cur_member->dynamic) {
7504  ao2_ref(cur_member, -1);
7505  continue;
7506  }
7507 
7508  ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s;%s;%d",
7509  ast_str_strlen(value) ? "|" : "",
7510  cur_member->interface,
7511  cur_member->penalty,
7512  cur_member->paused,
7513  cur_member->membername,
7514  cur_member->state_interface,
7515  cur_member->reason_paused,
7516  cur_member->wrapuptime);
7517 
7518  ao2_ref(cur_member, -1);
7519  }
7520  ao2_iterator_destroy(&mem_iter);
7521 
7522  if (ast_str_strlen(value) && !cur_member) {
7523  if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) {
7524  ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
7525  }
7526  } else {
7527  /* Delete the entry if the queue is empty or there is an error */
7528  ast_db_del(pm_family, pm_queue->name);
7529  }
7530 
7531  ast_free(value);
7532 }
int dynamic
Definition: app_queue.c:1735
int paused
Definition: app_queue.c:1738
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1730
int wrapuptime
Definition: app_queue.c:1742
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ao2_container * members
Definition: app_queue.c:1887
char membername[80]
Definition: app_queue.c:1732
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
int penalty
Definition: app_queue.c:1733
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
Support for dynamic strings.
Definition: strings.h:623
const ast_string_field name
Definition: app_queue.c:1829
char reason_paused[80]
Definition: app_queue.c:1739
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
static const char *const pm_family
Persistent Members astdb family.
Definition: app_queue.c:1587
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
static struct call_queue* find_load_queue_rt_friendly ( const char *  queuename)
static

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

References ao2_t_find, ast_atomic_fetchadd_int(), ast_config_destroy(), ast_config_new(), ast_debug, ast_load_realtime_multientry(), ast_variables_destroy(), find_queue_by_name_rt(), OBJ_POINTER, and call_queue::weight.

Referenced by __queues_show(), add_to_queue(), change_priority_caller_on_queue(), find_member_by_queuename_and_interface(), get_member_penalty(), queue_function_exists(), queue_function_mem_read(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_var(), qupd_exec(), reload_queue_members(), and request_withdraw_caller_from_queue().

3896 {
3897  struct ast_variable *queue_vars;
3898  struct ast_config *member_config = NULL;
3899  struct call_queue *q = NULL, tmpq = {
3900  .name = queuename,
3901  };
3902  int prev_weight = 0;
3903 
3904  /* Find the queue in the in-core list first. */
3905  q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
3906 
3907  if (!q || q->realtime) {
3908  /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
3909  queue operations while waiting for the DB.
3910 
3911  This will be two separate database transactions, so we might
3912  see queue parameters as they were before another process
3913  changed the queue and member list as it was after the change.
3914  Thus we might see an empty member list when a queue is
3915  deleted. In practise, this is unlikely to cause a problem. */
3916 
3917  queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
3918  if (queue_vars) {
3919  member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
3920  if (!member_config) {
3921  ast_debug(1, "No queue_members defined in config extconfig.conf\n");
3922  member_config = ast_config_new();
3923  }
3924  }
3925  if (q) {
3926  prev_weight = q->weight ? 1 : 0;
3927  queue_t_unref(q, "Need to find realtime queue");
3928  }
3929 
3930  q = find_queue_by_name_rt(queuename, queue_vars, member_config);
3931  ast_config_destroy(member_config);
3932  ast_variables_destroy(queue_vars);
3933 
3934  /* update the use_weight value if the queue's has gained or lost a weight */
3935  if (q) {
3936  if (!q->weight && prev_weight) {
3938  }
3939  if (q->weight && !prev_weight) {
3941  }
3942  }
3943  /* Other cases will end up with the proper value for use_weight */
3944  } else {
3945  update_realtime_members(q);
3946  }
3947  return q;
3948 }
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
#define OBJ_POINTER
Definition: astobj2.h:1150
Structure for variables, used for configurations and for channel variables.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
#define ast_debug(level,...)
Log a DEBUG message.
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3274
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
static struct call_queue * find_queue_by_name_rt(const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
Reload a single queue via realtime.
Definition: app_queue.c:3751
#define ao2_t_find(container, arg, flags, tag)
Definition: astobj2.h:1734
static int use_weight
Records that one or more queues use weight.
Definition: app_queue.c:1593
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
static struct member * find_member_by_queuename_and_interface ( const char *  queuename,
const char *  interface 
)
static

Find a member by looking up queuename and interface.

Returns
member or NULL if member not found.

Definition at line 11927 of file app_queue.c.

References find_load_queue_rt_friendly(), call_queue::members, and OBJ_KEY.

Referenced by rqm_exec().

11928 {
11929  struct member *mem = NULL;
11930  struct call_queue *q;
11931 
11932  if ((q = find_load_queue_rt_friendly(queuename))) {
11933  ao2_lock(q);
11934  mem = ao2_find(q->members, interface, OBJ_KEY);
11935  ao2_unlock(q);
11936  queue_t_unref(q, "Expiring temporary reference.");
11937  }
11938  return mem;
11939 }
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
#define OBJ_KEY
Definition: astobj2.h:1151
struct ao2_container * members
Definition: app_queue.c:1887
static struct call_queue* find_queue_by_name_rt ( const char *  queuename,
struct ast_variable queue_vars,
struct ast_config member_config 
)
static

Reload a single queue via realtime.

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

Return values
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 3751 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ao2_t_find, ast_category_browse(), ast_copy_string(), ast_debug, member::dead, init_queue(), member::interface, member::membername, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, queue_set_param(), member::realtime, rt_handle_member_record(), and ast_variable::value.

Referenced by find_load_queue_rt_friendly().

3752 {
3753  struct ast_variable *v;
3754  struct call_queue *q, tmpq = {
3755  .name = queuename,
3756  };
3757  struct member *m;
3758  struct ao2_iterator mem_iter;
3759  char *category = NULL;
3760  const char *tmp_name;
3761  char *tmp;
3762  char tmpbuf[64]; /* Must be longer than the longest queue param name. */
3763 
3764  /* Static queues override realtime. */
3765  if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
3766  ao2_lock(q);
3767  if (!q->realtime) {
3768  if (q->dead) {
3769  ao2_unlock(q);
3770  queue_t_unref(q, "Queue is dead; can't return it");
3771  return NULL;
3772  }
3773  ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
3774  ao2_unlock(q);
3775  return q;
3776  }
3777  } else if (!member_config) {
3778  /* Not found in the list, and it's not realtime ... */
3779  return NULL;
3780  }
3781  /* Check if queue is defined in realtime. */
3782  if (!queue_vars) {
3783  /* Delete queue from in-core list if it has been deleted in realtime. */
3784  if (q) {
3785  /*! \note Hmm, can't seem to distinguish a DB failure from a not
3786  found condition... So we might delete an in-core queue
3787  in case of DB failure. */
3788  ast_debug(1, "Queue %s not found in realtime.\n", queuename);
3789 
3790  q->dead = 1;
3791  /* Delete if unused (else will be deleted when last caller leaves). */
3792  queues_t_unlink(queues, q, "Unused; removing from container");
3793  ao2_unlock(q);
3794  queue_t_unref(q, "Queue is dead; can't return it");
3795  }
3796  return NULL;
3797  }
3798 
3799  /* Create a new queue if an in-core entry does not exist yet. */
3800  if (!q) {
3801  struct ast_variable *tmpvar = NULL;
3802  if (!(q = alloc_queue(queuename))) {
3803  return NULL;
3804  }
3805  ao2_lock(q);
3806  clear_queue(q);
3807  q->realtime = 1;
3808  /*Before we initialize the queue, we need to set the strategy, so that linear strategy
3809  * will allocate the members properly
3810  */
3811  for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
3812  if (!strcasecmp(tmpvar->name, "strategy")) {
3813  q->strategy = strat2int(tmpvar->value);
3814  if (q->strategy < 0) {
3815  ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3816  tmpvar->value, q->name);
3817  q->strategy = QUEUE_STRATEGY_RINGALL;
3818  }
3819  break;
3820  }
3821  }
3822  /* We traversed all variables and didn't find a strategy */
3823  if (!tmpvar) {
3824  q->strategy = QUEUE_STRATEGY_RINGALL;
3825  }
3826  queues_t_link(queues, q, "Add queue to container");
3827  }
3828  init_queue(q); /* Ensure defaults for all parameters not set explicitly. */
3829 
3830  memset(tmpbuf, 0, sizeof(tmpbuf));
3831  for (v = queue_vars; v; v = v->next) {
3832  /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
3833  if (strchr(v->name, '_')) {
3834  ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
3835  tmp_name = tmpbuf;
3836  tmp = tmpbuf;
3837  while ((tmp = strchr(tmp, '_'))) {
3838  *tmp++ = '-';
3839  }
3840  } else {
3841  tmp_name = v->name;
3842  }
3843 
3844  /* NULL values don't get returned from realtime; blank values should
3845  * still get set. If someone doesn't want a value to be set, they
3846  * should set the realtime column to NULL, not blank. */
3847  queue_set_param(q, tmp_name, v->value, -1, 0);
3848  }
3849 
3850  /* Temporarily set realtime members dead so we can detect deleted ones. */
3851  mem_iter = ao2_iterator_init(q->members, 0);
3852  while ((m = ao2_iterator_next(&mem_iter))) {
3853  if (m->realtime) {
3854  m->dead = 1;
3855  }
3856  ao2_ref(m, -1);
3857  }
3858  ao2_iterator_destroy(&mem_iter);
3859 
3860  while ((category = ast_category_browse(member_config, category))) {
3861  rt_handle_member_record(q, category, member_config);
3862  }
3863 
3864  /* Delete all realtime members that have been deleted in DB. */
3865  mem_iter = ao2_iterator_init(q->members, 0);
3866  while ((m = ao2_iterator_next(&mem_iter))) {
3867  if (m->dead) {
3868  if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
3869  ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
3870  } else {
3871  ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
3872  }
3873  member_remove_from_queue(q, m);
3874  }
3875  ao2_ref(m, -1);
3876  }
3877  ao2_iterator_destroy(&mem_iter);
3878 
3879  ao2_unlock(q);
3880 
3881  return q;
3882 }
struct ast_variable * next
#define OBJ_POINTER
Definition: astobj2.h:1150
Structure for variables, used for configurations and for channel variables.
static int log_membername_as_agent
queues.conf [general] option
Definition: app_queue.c:1614
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int realtime
Definition: app_queue.c:1736
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
unsigned int dead
Definition: app_queue.c:1748
struct ao2_container * members
Definition: app_queue.c:1887
static void rt_handle_member_record(struct call_queue *q, char *category, struct ast_config *member_config)
Find rt member record to update otherwise create one.
Definition: app_queue.c:3578
char membername[80]
Definition: app_queue.c:1732
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
Configure a queue parameter.
Definition: app_queue.c:3332
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ast_debug(level,...)
Log a DEBUG message.
const ast_string_field name
Definition: app_queue.c:1829
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define ao2_t_find(container, arg, flags, tag)
Definition: astobj2.h:1734
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Definition: app_queue.c:2949
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
static int get_member_penalty ( char *  queuename,
char *  interface 
)
static

Gets members penalty.

Returns
Return the members penalty or RESULT_FAILURE on error.

Definition at line 8069 of file app_queue.c.

References ao2_ref, find_load_queue_rt_friendly(), and member::penalty.

Referenced by queue_function_memberpenalty_read().

8070 {
8071  int foundqueue = 0, penalty;
8072  struct call_queue *q;
8073  struct member *mem;
8074 
8075  if ((q = find_load_queue_rt_friendly(queuename))) {
8076  foundqueue = 1;
8077  ao2_lock(q);
8078  if ((mem = interface_exists(q, interface))) {
8079  penalty = mem->penalty;
8080  ao2_ref(mem, -1);
8081  ao2_unlock(q);
8082  queue_t_unref(q, "Search complete");
8083  return penalty;
8084  }
8085  ao2_unlock(q);
8086  queue_t_unref(q, "Search complete");
8087  }
8088 
8089  /* some useful debugging */
8090  if (foundqueue) {
8091  ast_log (LOG_ERROR, "Invalid queuename\n");
8092  } else {
8093  ast_log (LOG_ERROR, "Invalid interface\n");
8094  }
8095 
8096  return RESULT_FAILURE;
8097 }
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
int penalty
Definition: app_queue.c:1733
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
static int get_member_status ( struct call_queue q,
int  max_penalty,
int  min_penalty,
int  raise_penalty,
enum empty_conditions  conditions,
int  devstate 
)
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 2422 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_RINGING, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, get_wrapuptime(), member::lastcall, member::membername, call_queue::members, member::paused, member::penalty, queue_ent::raise_penalty, member::state_interface, and member::status.

Referenced by queue_exec(), and wait_our_turn().

2423 {
2424  struct member *member;
2425  struct ao2_iterator mem_iter;
2426 
2427  ao2_lock(q);
2428  mem_iter = ao2_iterator_init(q->members, 0);
2429  for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
2430  int penalty = member->penalty;
2431  if (raise_penalty != INT_MAX && penalty < raise_penalty) {
2432  ast_debug(4, "%s is having his penalty raised up from %d to %d\n", member->membername, penalty, raise_penalty);
2433  penalty = raise_penalty;
2434  }
2435  if ((max_penalty != INT_MAX && penalty > max_penalty) || (min_penalty != INT_MAX && penalty < min_penalty)) {
2436  if (conditions & QUEUE_EMPTY_PENALTY) {
2437  ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
2438  continue;
2439  }
2440  }
2441 
2442  switch (devstate ? ast_device_state(member->state_interface) : member->status) {
2443  case AST_DEVICE_INVALID:
2444  if (conditions & QUEUE_EMPTY_INVALID) {
2445  ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
2446  break;
2447  }
2448  goto default_case;
2450  if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
2451  ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
2452  break;
2453  }
2454  goto default_case;
2455  case AST_DEVICE_INUSE:
2456  if (conditions & QUEUE_EMPTY_INUSE) {
2457  ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
2458  break;
2459  }
2460  goto default_case;
2461  case AST_DEVICE_RINGING:
2462  if (conditions & QUEUE_EMPTY_RINGING) {
2463  ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
2464  break;
2465  }
2466  goto default_case;
2467  case AST_DEVICE_UNKNOWN:
2468  if (conditions & QUEUE_EMPTY_UNKNOWN) {
2469  ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
2470  break;
2471  }
2472  /* Fall-through */
2473  default:
2474  default_case:
2475  if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
2476  ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
2477  break;
2478  } else if ((conditions & QUEUE_EMPTY_WRAPUP)
2479  && member->lastcall
2480  && get_wrapuptime(q, member)
2481  && (time(NULL) - get_wrapuptime(q, member) < member->lastcall)) {
2482  ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n",
2483  member->membername, (int) (time(NULL) - member->lastcall), get_wrapuptime(q, member));
2484  break;
2485  } else {
2486  ao2_ref(member, -1);
2487  ao2_iterator_destroy(&mem_iter);
2488  ao2_unlock(q);
2489  ast_debug(4, "%s is available.\n", member->membername);
2490  return 0;
2491  }
2492  break;
2493  }
2494  }
2495  ao2_iterator_destroy(&mem_iter);
2496  ao2_unlock(q);
2497 
2498  if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
2499  /* member state still may be RINGING due to lag in event message - check again with device state */
2500  return get_member_status(q, max_penalty, min_penalty, raise_penalty, conditions, 1);
2501  }
2502  return -1;
2503 }
ast_device_state
Device States.
Definition: devicestate.h:52
int paused
Definition: app_queue.c:1738
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1730
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ao2_container * members
Definition: app_queue.c:1887
char membername[80]
Definition: app_queue.c:1732
int penalty
Definition: app_queue.c:1733
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define ast_debug(level,...)
Log a DEBUG message.
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
Definition: app_queue.c:1991
int status
Definition: app_queue.c:1737
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
Check if members are available.
Definition: app_queue.c:2422
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
time_t lastcall
Definition: app_queue.c:1744
static int get_wrapuptime ( struct call_queue q,
struct member member 
)
static

Return wrapuptime.

This function checks if wrapuptime in member is set and return this value. Otherwise return value the wrapuptime in the queue configuration

Returns
integer value

Definition at line 1991 of file app_queue.c.

References member::wrapuptime, and call_queue::wrapuptime.

Referenced by get_member_status(), and queue_function_mem_read().

1992 {
1993  if (member->wrapuptime) {
1994  return member->wrapuptime;
1995  }
1996  return q->wrapuptime;
1997 }
int wrapuptime
Definition: app_queue.c:1870
int wrapuptime
Definition: app_queue.c:1742
static void handle_attended_transfer ( void *  userdata,
struct stasis_subscription sub,
struct stasis_message msg 
)
static

Handle an attended transfer event.

This event is important in order to be able to log the end of the call to the queue log and to stasis.

Parameters
userdataData pertaining to the particular call in the queue.
subThe stasis subscription on which the message occurred.
msgThe stasis message for the attended transfer event.

Definition at line 6468 of file app_queue.c.

References AST_BRIDGE_TRANSFER_SUCCESS, ast_channel_snapshot_get_latest(), ast_debug, queue_stasis_data::bridge_uniqueid, queue_stasis_data::callcompletedinsl, queue_stasis_data::caller_uniqueid, ast_attended_transfer_message::dest_type, queue_stasis_data::dying, queue_stasis_data::holdstart, queue_stasis_data::member, queue_stasis_data::member_uniqueid, call_queue::name, queue_stasis_data::queue, RAII_VAR, ast_attended_transfer_message::result, send_agent_complete(), queue_stasis_data::starttime, stasis_message_data(), ast_attended_transfer_message::to_transfer_target, ast_attended_transfer_message::to_transferee, ast_bridge_snapshot::uniqueid, and update_queue().

6470 {
6471  struct queue_stasis_data *queue_data = userdata;
6472  struct ast_attended_transfer_message *atxfer_msg = stasis_message_data(msg);
6473  RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6474  RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6475 
6476  if (atxfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS ||
6477  atxfer_msg->dest_type == AST_ATTENDED_TRANSFER_DEST_THREEWAY) {
6478  return;
6479  }
6480 
6481  ao2_lock(queue_data);
6482 
6483  if (queue_data->dying) {
6484  ao2_unlock(queue_data);
6485  return;
6486  }
6487 
6488  if (ast_strlen_zero(queue_data->bridge_uniqueid)) {
6489  ao2_unlock(queue_data);
6490  return;
6491  }
6492 
6493  if ((!atxfer_msg->to_transferee.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6494  atxfer_msg->to_transferee.bridge_snapshot->uniqueid)) &&
6495  (!atxfer_msg->to_transfer_target.bridge_snapshot || strcmp(queue_data->bridge_uniqueid,
6496  atxfer_msg->to_transfer_target.bridge_snapshot->uniqueid))) {
6497  ao2_unlock(queue_data);
6498  return;
6499  }
6500 
6501  caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6502  member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6503 
6504  ao2_unlock(queue_data);
6505 
6506  ast_debug(3, "Detected attended transfer in queue %s\n", queue_data->queue->name);
6507  log_attended_transfer(queue_data, atxfer_msg);
6508 
6509  send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6510  queue_data->holdstart, queue_data->starttime, TRANSFER);
6511  update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6512  queue_data->starttime);
6513  remove_stasis_subscriptions(queue_data);
6514 }
Message representing attended transfer.
Structure representing a snapshot of channel state.
struct ast_bridge_channel_snapshot_pair to_transferee
struct ast_bridge_channel_snapshot_pair to_transfer_target
enum ast_transfer_result result
struct call_queue * queue
Definition: app_queue.c:6232
struct member * member
Definition: app_queue.c:6234
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition: app_queue.c:5972
#define ast_debug(level,...)
Log a DEBUG message.
const ast_string_field member_uniqueid
Definition: app_queue.c:6230
enum ast_attended_transfer_dest_type dest_type
const ast_string_field bridge_uniqueid
Definition: app_queue.c:6230
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
const ast_string_field name
Definition: app_queue.c:1829
const ast_string_field caller_uniqueid
Definition: app_queue.c:6230
const ast_string_field uniqueid
Definition: bridge.h:328
static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart, time_t callstart, enum agent_complete_reason rsn)
Send out AMI message with member call completion status information.
Definition: app_queue.c:6129
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
User data for stasis subscriptions used for queue calls.
Definition: app_queue.c:6222
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
static void handle_blind_transfer ( void *  userdata,
struct stasis_subscription sub,
struct stasis_message msg 
)
static

Handle a blind transfer event.

This event is important in order to be able to log the end of the call to the queue log and to stasis.

Parameters
userdataData pertaining to the particular call in the queue.
subThe stasis subscription on which the message occurred.
msgThe stasis message for the blind transfer event

Definition at line 6409 of file app_queue.c.

References AST_BRIDGE_TRANSFER_SUCCESS, ast_channel_snapshot_get_latest(), ast_debug, ast_blind_transfer_message::bridge, queue_stasis_data::bridge_uniqueid, queue_stasis_data::callcompletedinsl, queue_stasis_data::caller_pos, queue_stasis_data::caller_uniqueid, ast_blind_transfer_message::context, queue_ent::context, queue_stasis_data::dying, ast_blind_transfer_message::exten, queue_stasis_data::holdstart, queue_stasis_data::member, queue_stasis_data::member_uniqueid, member::membername, call_queue::name, queue_stasis_data::queue, RAII_VAR, ast_blind_transfer_message::result, send_agent_complete(), queue_stasis_data::starttime, stasis_message_data(), ast_bridge_snapshot::uniqueid, and update_queue().

6411 {
6412  struct queue_stasis_data *queue_data = userdata;
6413  struct ast_blind_transfer_message *transfer_msg = stasis_message_data(msg);
6414  const char *exten;
6415  const char *context;
6416  RAII_VAR(struct ast_channel_snapshot *, caller_snapshot, NULL, ao2_cleanup);
6417  RAII_VAR(struct ast_channel_snapshot *, member_snapshot, NULL, ao2_cleanup);
6418 
6419  if (transfer_msg->result != AST_BRIDGE_TRANSFER_SUCCESS) {
6420  return;
6421  }
6422 
6423  ao2_lock(queue_data);
6424 
6425  if (queue_data->dying) {
6426  ao2_unlock(queue_data);
6427  return;
6428  }
6429 
6430  if (ast_strlen_zero(queue_data->bridge_uniqueid) ||
6431  strcmp(queue_data->bridge_uniqueid, transfer_msg->bridge->uniqueid)) {
6432  ao2_unlock(queue_data);
6433  return;
6434  }
6435 
6436  caller_snapshot = ast_channel_snapshot_get_latest(queue_data->caller_uniqueid);
6437  member_snapshot = ast_channel_snapshot_get_latest(queue_data->member_uniqueid);
6438 
6439  ao2_unlock(queue_data);
6440 
6441  exten = transfer_msg->exten;
6442  context = transfer_msg->context;
6443 
6444  ast_debug(3, "Detected blind transfer in queue %s\n", queue_data->queue->name);
6445  ast_queue_log(queue_data->queue->name, queue_data->caller_uniqueid, queue_data->member->membername,
6446  "BLINDTRANSFER", "%s|%s|%ld|%ld|%d",
6447  exten, context,
6448  (long) (queue_data->starttime - queue_data->holdstart),
6449  (long) (time(NULL) - queue_data->starttime), queue_data->caller_pos);
6450 
6451  send_agent_complete(queue_data->queue->name, caller_snapshot, member_snapshot, queue_data->member,
6452  queue_data->holdstart, queue_data->starttime, TRANSFER);
6453  update_queue(queue_data->queue, queue_data->member, queue_data->callcompletedinsl,
6454  queue_data->starttime);
6455  remove_stasis_subscriptions(queue_data);
6456 }
char exten[AST_MAX_EXTENSION]
Message published during a blind transfer.
Structure representing a snapshot of channel state.
char membername[80]
Definition: app_queue.c:1732
struct call_queue * queue
Definition: app_queue.c:6232
struct member * member
Definition: app_queue.c:6234
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition: app_queue.c:5972
struct ast_bridge_snapshot * bridge
#define ast_debug(level,...)
Log a DEBUG message.
const ast_string_field member_uniqueid
Definition: app_queue.c:6230
const ast_string_field bridge_uniqueid
Definition: app_queue.c:6230
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
const ast_string_field name
Definition: app_queue.c:1829
const ast_string_field caller_uniqueid
Definition: app_queue.c:6230
char context[AST_MAX_CONTEXT]
const ast_string_field uniqueid
Definition: bridge.h:328
static void send_agent_complete(const char *queuename, struct ast_channel_snapshot *caller, struct ast_channel_snapshot *peer, const struct member *member, time_t holdstart, time_t callstart, enum agent_complete_reason rsn)
Send out AMI message with member call completion status information.
Definition: app_queue.c:6129
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
User data for stasis subscriptions used for queue calls.
Definition: app_queue.c:6222
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
enum ast_transfer_result result
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 2949 of file app_queue.c.

References call_queue::announce_to_first_user, call_queue::announcefrequency, call_queue::announceposition_only_up, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, AO2_ALLOC_OPT_LOCK_MUTEX, ao2_container_alloc_hash, ao2_container_alloc_list, AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), 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, DEFAULT_MIN_ANNOUNCE_FREQUENCY, call_queue::log_restricted_caller_id, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::name, call_queue::numperiodicannounce, call_queue::penaltymemberslimit, call_queue::periodicannouncefrequency, call_queue::periodicannouncestartdelay, call_queue::randomperiodicannounce, call_queue::retry, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::sound_periodicannounce, call_queue::timeout, call_queue::timeoutpriority, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

2950 {
2951  int i;
2952  struct penalty_rule *pr_iter;
2953 
2954  q->dead = 0;
2955  q->retry = DEFAULT_RETRY;
2956  q->timeout = DEFAULT_TIMEOUT;
2957  q->maxlen = 0;
2958 
2959  ast_string_field_set(q, announce, "");
2960  ast_string_field_set(q, context, "");
2961  ast_string_field_set(q, membergosub, "");
2962  ast_string_field_set(q, defaultrule, "");
2963 
2964  q->announcefrequency = 0;
2966  q->announceholdtime = 1;
2967  q->announceposition_only_up = 0;
2968  q->announcepositionlimit = 10; /* Default 10 positions */
2969  q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
2970  q->roundingseconds = 0; /* Default - don't announce seconds */
2971  q->servicelevel = 0;
2972  q->ringinuse = 1;
2973  q->announce_to_first_user = 0;
2974  q->setinterfacevar = 0;
2975  q->setqueuevar = 0;
2976  q->setqueueentryvar = 0;
2978  q->monfmt[0] = '\0';
2979  q->reportholdtime = 0;
2980  q->wrapuptime = 0;
2981  q->penaltymemberslimit = 0;
2982  q->joinempty = 0;
2983  q->leavewhenempty = 0;
2984  q->memberdelay = 0;
2985  q->weight = 0;
2986  q->timeoutrestart = 0;
2989  q->randomperiodicannounce = 0;
2990  q->numperiodicannounce = 0;
2991  q->relativeperiodicannounce = 0;
2992  q->autopause = QUEUE_AUTOPAUSE_OFF;
2993  q->autopausebusy = 0;
2994  q->autopauseunavail = 0;
2995  q->timeoutpriority = TIMEOUT_PRIORITY_APP;
2996  q->autopausedelay = 0;
2997  q->log_restricted_caller_id = 1;
2998  if (!q->members) {
2999  if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED) {
3000  /* linear strategy depends on order, so we have to place all members in a list */
3001  q->members = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, member_cmp_fn);
3002  } else {
3004  member_hash_fn, NULL, member_cmp_fn);
3005  }
3006  }
3007  q->found = 1;
3008 
3009  ast_string_field_set(q, moh, "");
3010  ast_string_field_set(q, sound_next, "queue-youarenext");
3011  ast_string_field_set(q, sound_thereare, "queue-thereare");
3012  ast_string_field_set(q, sound_calls, "queue-callswaiting");
3013  ast_string_field_set(q, queue_quantity1, "queue-quantity1");
3014  ast_string_field_set(q, queue_quantity2, "queue-quantity2");
3015  ast_string_field_set(q, sound_holdtime, "queue-holdtime");
3016  ast_string_field_set(q, sound_minutes, "queue-minutes");
3017  ast_string_field_set(q, sound_minute, "queue-minute");
3018  ast_string_field_set(q, sound_seconds, "queue-seconds");
3019  ast_string_field_set(q, sound_thanks, "queue-thankyou");
3020  ast_string_field_set(q, sound_callerannounce, "");
3021  ast_string_field_set(q, sound_reporthold, "queue-reporthold");
3022 
3023  if (!q->sound_periodicannounce[0]) {
3025  }
3026 
3027  if (q->sound_periodicannounce[0]) {
3028  ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
3029  }
3030 
3031  for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
3032  if (q->sound_periodicannounce[i]) {
3033  ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
3034  }
3035  }
3036 
3037  while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) {
3038  ast_free(pr_iter);
3039  }
3040 
3041  /* On restart assume no members are available.
3042  * The queue_avail hint is a boolean state to indicate whether a member is available or not.
3043  *
3044  * This seems counter intuitive, but is required to light a BLF
3045  * AST_DEVICE_INUSE indicates no members are available.
3046  * AST_DEVICE_NOT_INUSE indicates a member is available.
3047  */
3049 }
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
Definition: app_queue.c:1831
int servicelevel
Definition: app_queue.c:1865
#define MAX_PERIODIC_ANNOUNCEMENTS
Definition: app_queue.c:1556
int wrapuptime
Definition: app_queue.c:1870
int penaltymemberslimit
Definition: app_queue.c:1871
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY
The minimum number of seconds between position announcements.
Definition: app_queue.c:1561
unsigned int announceposition_only_up
Definition: app_queue.c:1843
int minannouncefrequency
Definition: app_queue.c:1854
int autopause
Definition: app_queue.c:1876
#define ao2_container_alloc_list(ao2_options, container_options, sort_fn, cmp_fn)
Allocate and initialize a list container.
Definition: astobj2.h:1327
int periodicannouncestartdelay
Definition: app_queue.c:1855
int log_restricted_caller_id
Definition: app_queue.c:1885
unsigned int announce_to_first_user
Definition: app_queue.c:1834
struct ao2_container * members
Definition: app_queue.c:1887
char monfmt[8]
Definition: app_queue.c:1867
int announcepositionlimit
Definition: app_queue.c:1852
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
int periodicannouncefrequency
Definition: app_queue.c:1856
int memberdelay
Definition: app_queue.c:1882
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
struct penalty_rule::@52 list
static int autofill_default
queues.conf [general] option
Definition: app_queue.c:1596
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
#define ANNOUNCEPOSITION_YES
Definition: app_queue.c:1786
int autopausedelay
Definition: app_queue.c:1877
const ast_string_field name
Definition: app_queue.c:1829
int autofill
Definition: app_queue.c:1883
int roundingseconds
Definition: app_queue.c:1859
int timeoutpriority
Definition: app_queue.c:1878
struct call_queue::@54 rules
int randomperiodicannounce
Definition: app_queue.c:1858
int numperiodicannounce
Definition: app_queue.c:1857
int announcefrequency
Definition: app_queue.c:1853
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
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 formatting, 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 3083 of file app_queue.c.

References ast_calloc, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_strdupa, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, penalty_rule::raise_relative, penalty_rule::raise_value, and penalty_rule::time.

Referenced by reload_queue_rules().

3084 {
3085  char *timestr, *maxstr, *minstr, *raisestr, *contentdup;
3086  struct penalty_rule *rule = NULL, *rule_iter;
3087  struct rule_list *rl_iter;
3088  int penaltychangetime, inserted = 0;
3089 
3090  if (!(rule = ast_calloc(1, sizeof(*rule)))) {
3091  return -1;
3092  }
3093 
3094  contentdup = ast_strdupa(content);
3095 
3096  if (!(maxstr = strchr(contentdup, ','))) {
3097  ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
3098  ast_free(rule);
3099  return -1;
3100  }
3101 
3102  *maxstr++ = '\0';
3103  if ((minstr = strchr(maxstr,','))) {
3104  *minstr++ = '\0';
3105  if ((raisestr = strchr(minstr,','))) {
3106  *raisestr++ = '\0';
3107  }
3108  } else {
3109  raisestr = NULL;
3110  }
3111 
3112  timestr = contentdup;
3113  if ((penaltychangetime = atoi(timestr)) < 0) {
3114  ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
3115  ast_free(rule);
3116  return -1;
3117  }
3118 
3119  rule->time = penaltychangetime;
3120 
3121  /* The last check will evaluate true if either no penalty change is indicated for a given rule
3122  * OR if a min penalty change is indicated but no max penalty change is */
3123  if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
3124  rule->max_relative = 1;
3125  }
3126 
3127  rule->max_value = atoi(maxstr);
3128 
3129  if (!ast_strlen_zero(minstr)) {
3130  if (*minstr == '+' || *minstr == '-') {
3131  rule->min_relative = 1;
3132  }
3133  rule->min_value = atoi(minstr);
3134  } else { /*there was no minimum specified, so assume this means no change*/
3135  rule->min_relative = 1;
3136  }
3137 
3138  if (!ast_strlen_zero(raisestr)) {
3139  if (*raisestr == '+' || *raisestr == '-') {
3140  rule->raise_relative = 1;
3141  }
3142  rule->raise_value = atoi(raisestr);
3143  } else { /*there was no raise specified, so assume this means no change*/
3144  rule->raise_relative = 1;
3145  }
3146 
3147  /*We have the rule made, now we need to insert it where it belongs*/
3148  AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
3149  if (strcasecmp(rl_iter->name, list_name)) {
3150  continue;
3151  }
3152 
3153  AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
3154  if (rule->time < rule_iter->time) {
3155  AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
3156  inserted = 1;
3157  break;
3158  }
3159  }
3161 
3162  if (!inserted) {
3163  AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
3164  inserted = 1;
3165  }
3166 
3167  break;
3168  }
3169 
3170  if (!inserted) {
3171  ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
3172  ast_free(rule);
3173  return -1;
3174  }
3175  return 0;
3176 }
int max_relative
Definition: app_queue.c:1780
int min_relative
Definition: app_queue.c:1781
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int raise_relative
Definition: app_queue.c:1782
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
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 5745 of file app_queue.c.

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

5746 {
5747  struct queue_ent *ch;
5748  int res;
5749  int avl;
5750  int idx = 0;
5751  /* This needs a lock. How many members are available to be served? */
5752  ao2_lock(qe->parent);
5753 
5754  avl = num_available_members(qe->parent);
5755 
5756  ch = qe->parent->head;
5757 
5758  ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
5759 
5760  while ((idx < avl) && (ch) && (ch != qe)) {
5761  if (!ch->pending) {
5762  idx++;
5763  }
5764  ch = ch->next;
5765  }
5766 
5767  ao2_unlock(qe->parent);
5768  /* If the queue entry is within avl [the number of available members] calls from the top ...
5769  * Autofill and position check added to support autofill=no (as only calls
5770  * from the front of the queue are valid when autofill is disabled)
5771  */
5772  if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
5773  ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
5774  res = 1;
5775  } else {
5776  ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
5777  res = 0;
5778  }
5779 
5780  /* Update realtime members if this is the first call and number of avalable members is 0 */
5781  if (avl == 0 && qe->pos == 1) {
5782  update_realtime_members(qe->parent);
5783  }
5784 
5785  return res;
5786 }
struct call_queue * parent
Definition: app_queue.c:1693
struct ast_channel * chan
Definition: app_queue.c:1720
struct queue_ent * head
Definition: app_queue.c:1888
#define ast_debug(level,...)
Log a DEBUG message.
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
Definition: app_queue.c:4509
int autofill
Definition: app_queue.c:1883
int pending
Definition: app_queue.c:1709
struct queue_ent * next
Definition: app_queue.c:1723
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 4372 of file app_queue.c.

References ast_channel_publish_cached_blob(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_json_pack(), ast_json_unref(), AST_LIST_REMOVE_HEAD, ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::head, call_queue::name, queue_ent::next, queue_ent::parent, pbx_builtin_setvar_helper(), queue_ent::pos, queue_ent::pr, queue_ent::qe_rules, and RAII_VAR.

Referenced by queue_exec().

4373 {
4374  struct call_queue *q;
4375  struct queue_ent *current, *prev = NULL;
4376  struct penalty_rule *pr_iter;
4377  int pos = 0;
4378 
4379  if (!(q = qe->parent)) {
4380  return;
4381  }
4382  queue_t_ref(q, "Copy queue pointer from queue entry");
4383  ao2_lock(q);
4384 
4385  prev = NULL;
4386  for (current = q->head; current; current = current->next) {
4387  if (current == qe) {
4388  RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4389  char posstr[20];
4390  q->count--;
4391  if (!q->count) {
4393  }
4394 
4395  blob = ast_json_pack("{s: s, s: i, s: i}",
4396  "Queue", q->name,
4397  "Position", qe->pos,
4398  "Count", q->count);
4399  ast_channel_publish_cached_blob(qe->chan, queue_caller_leave_type(), blob);
4400  ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, ast_channel_name(qe->chan));
4401  /* Take us out of the queue */
4402  if (prev) {
4403  prev->next = current->next;
4404  } else {
4405  q->head = current->next;
4406  }
4407  /* Free penalty rules */
4408  while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) {
4409  ast_free(pr_iter);
4410  }
4411  qe->pr = NULL;
4412  snprintf(posstr, sizeof(posstr), "%d", qe->pos);
4413  pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
4414  } else {
4415  /* Renumber the people after us in the queue based on a new count */
4416  current->pos = ++pos;
4417  prev = current;
4418  }
4419  }
4420  ao2_unlock(q);
4421 
4422  /*If the queue is a realtime queue, check to see if it's still defined in real time*/
4423  if (q->realtime) {
4424  struct ast_variable *var;
4425  if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
4426  q->dead = 1;
4427  } else {
4428  ast_variables_destroy(var);
4429  }
4430  }
4431 
4432  if (q->dead) {
4433  /* It's dead and nobody is in it, so kill it */
4434  queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
4435  }
4436  /* unref the explicit ref earlier in the function */
4437  queue_t_unref(q, "Expire copied reference");
4438 }
struct call_queue * parent
Definition: app_queue.c:1693
struct ast_channel * chan
Definition: app_queue.c:1720
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
void ast_channel_publish_cached_blob(struct ast_channel *chan, struct stasis_message_type *type, struct ast_json *blob)
Publish a channel blob message using the latest snapshot from the cache.
Structure for variables, used for configurations and for channel variables.
struct queue_ent * head
Definition: app_queue.c:1888
struct queue_ent::@51 qe_rules
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
struct penalty_rule::@52 list
const ast_string_field name
Definition: app_queue.c:1829
struct penalty_rule * pr
Definition: app_queue.c:1722
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Abstract JSON element (object, array, string, int, ...).
struct queue_ent * next
Definition: app_queue.c:1723
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
static int load_module ( void  )
static

Load the module.

Module loading including tests for configuration or dependencies. This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails tests return AST_MODULE_LOAD_FAILURE. If the module can not load the configuration file or other non-critical problem return AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.

Definition at line 11759 of file app_queue.c.

References AO2_ALLOC_OPT_LOCK_MUTEX, ao2_container_alloc_hash, aqm_exec(), ast_channel_agent_login_type(), ast_channel_agent_logoff_type(), ast_channel_topic_all(), ast_cli_register_multiple, ast_config_destroy(), ast_custom_function_register, ast_device_state_message_type(), ast_device_state_topic_all(), ast_load_realtime_multientry(), ast_manager_get_topic(), ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_queue_topic_all(), ast_realtime_require_field(), ast_register_application_xml, device_state_cb(), manager_queues_status(), manager_queues_summary(), manager_topic, pqm_exec(), ql_exec(), queue_exec(), qupd_exec(), reload_handler(), reload_queue_members(), rqm_exec(), stasis_forward_all(), stasis_message_router_add(), STASIS_MESSAGE_TYPE_INIT, stasis_subscription_accept_message_type(), STASIS_SUBSCRIPTION_FILTER_SELECTIVE, stasis_subscription_set_filter(), and upqm_exec().

11760 {
11761  int err = 0;
11762  struct ast_flags mask = {AST_FLAGS_ALL, };
11763  struct ast_config *member_config;
11764  struct stasis_topic *queue_topic;
11765  struct stasis_topic *manager_topic;
11766 
11767  queues = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAX_QUEUE_BUCKETS,
11768  queue_hash_cb, NULL, queue_cmp_cb);
11769  if (!queues) {
11770  return AST_MODULE_LOAD_DECLINE;
11771  }
11772 
11774  MAX_CALL_ATTEMPT_BUCKETS, pending_members_hash, NULL, pending_members_cmp);
11775  if (!pending_members) {
11776  unload_module();
11777  return AST_MODULE_LOAD_DECLINE;
11778  }
11779 
11780  use_weight = 0;
11781 
11782  if (reload_handler(0, &mask, NULL)) {
11783  unload_module();
11784  return AST_MODULE_LOAD_DECLINE;
11785  }
11786 
11787  ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, "reason_paused", RQ_CHAR, 80, SENTINEL);
11788 
11789  /*
11790  * This section is used to determine which name for 'ringinuse' to use in realtime members
11791  * Necessary for supporting older setups.
11792  *
11793  * It also checks if 'reason_paused' exists in the realtime backend
11794  */
11795  member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name LIKE", "%", SENTINEL);
11796  if (!member_config) {
11797  realtime_ringinuse_field = "ringinuse";
11798  } else {
11799  const char *config_val;
11800 
11801  if ((config_val = ast_variable_retrieve(member_config, NULL, "ringinuse"))) {
11802  ast_log(LOG_NOTICE, "ringinuse field entries found in queue_members table. Using 'ringinuse'\n");
11803  realtime_ringinuse_field = "ringinuse";
11804  } else if ((config_val = ast_variable_retrieve(member_config, NULL, "ignorebusy"))) {
11805  ast_log(LOG_NOTICE, "ignorebusy field found in queue_members table with no ringinuse field. Using 'ignorebusy'\n");
11806  realtime_ringinuse_field = "ignorebusy";
11807  } else {
11808  ast_log(LOG_NOTICE, "No entries were found for ringinuse/ignorebusy in queue_members table. Using 'ringinuse'\n");
11809  realtime_ringinuse_field = "ringinuse";
11810  }
11811 
11812  if (ast_variable_retrieve(member_config, NULL, "reason_paused")) {
11814  }
11815  }
11816  ast_config_destroy(member_config);
11817 
11820  }
11821 
11822  err |= ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
11824  err |= ast_register_application_xml(app_aqm, aqm_exec);
11825  err |= ast_register_application_xml(app_rqm, rqm_exec);
11826  err |= ast_register_application_xml(app_pqm, pqm_exec);
11827  err |= ast_register_application_xml(app_upqm, upqm_exec);
11828  err |= ast_register_application_xml(app_ql, ql_exec);
11829  err |= ast_register_application_xml(app_qupd, qupd_exec);
11830  err |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
11831  err |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
11832  err |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
11833  err |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
11834  err |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
11835  err |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
11836  err |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
11837  err |= ast_manager_register_xml("QueueMemberRingInUse", EVENT_FLAG_AGENT, manager_queue_member_ringinuse);
11838  err |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
11839  err |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
11840  err |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
11841  err |= ast_manager_register_xml("QueueChangePriorityCaller", 0, manager_change_priority_caller_on_queue);
11842  err |= ast_manager_register_xml("QueueWithdrawCaller", 0, manager_request_withdraw_caller_from_queue);
11843  err |= ast_custom_function_register(&queuevar_function);
11844  err |= ast_custom_function_register(&queueexists_function);
11845  err |= ast_custom_function_register(&queuemembercount_function);
11846  err |= ast_custom_function_register(&queuemembercount_dep);
11847  err |= ast_custom_function_register(&queuememberlist_function);
11848  err |= ast_custom_function_register(&queuegetchannel_function);
11849  err |= ast_custom_function_register(&queuewaitingcount_function);
11850  err |= ast_custom_function_register(&queuememberpenalty_function);
11851 
11852  /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
11853  device_state_sub = stasis_subscribe(ast_device_state_topic_all(), device_state_cb, NULL);
11854  if (!device_state_sub) {
11855  err = -1;
11856  }
11859 
11860  manager_topic = ast_manager_get_topic();
11861  queue_topic = ast_queue_topic_all();
11862  if (!manager_topic || !queue_topic) {
11863  unload_module();
11864  return AST_MODULE_LOAD_DECLINE;
11865  }
11866  topic_forwarder = stasis_forward_all(queue_topic, manager_topic);
11867  if (!topic_forwarder) {
11868  unload_module();
11869  return AST_MODULE_LOAD_DECLINE;
11870  }
11871 
11874  unload_module();
11875  return AST_MODULE_LOAD_DECLINE;
11876  }
11877  agent_router = stasis_message_router_create(ast_channel_topic_all());
11878  if (!agent_router) {
11879  unload_module();
11880  return AST_MODULE_LOAD_DECLINE;
11881  }
11882  err |= stasis_message_router_add(agent_router,
11884  queue_agent_cb,
11885  NULL);
11886  err |= stasis_message_router_add(agent_router,
11888  queue_agent_cb,
11889  NULL);
11890 
11891  err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_join_type);
11892  err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_leave_type);
11893  err |= STASIS_MESSAGE_TYPE_INIT(queue_caller_abandon_type);
11894 
11895  err |= STASIS_MESSAGE_TYPE_INIT(queue_member_status_type);
11896  err |= STASIS_MESSAGE_TYPE_INIT(queue_member_added_type);
11897  err |= STASIS_MESSAGE_TYPE_INIT(queue_member_removed_type);
11898  err |= STASIS_MESSAGE_TYPE_INIT(queue_member_pause_type);
11899  err |= STASIS_MESSAGE_TYPE_INIT(queue_member_penalty_type);
11900  err |= STASIS_MESSAGE_TYPE_INIT(queue_member_ringinuse_type);
11901 
11902  err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_called_type);
11903  err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_connect_type);
11904  err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_complete_type);
11905  err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_dump_type);
11906  err |= STASIS_MESSAGE_TYPE_INIT(queue_agent_ringnoanswer_type);
11907 
11908  if (err) {
11909  unload_module();
11910  return AST_MODULE_LOAD_DECLINE;
11911  }
11912  return AST_MODULE_LOAD_SUCCESS;
11913 }
static int qupd_exec(struct ast_channel *chan, const char *data)
Update Queue with data of an outgoing call.
Definition: app_queue.c:11591
static int rqm_exec(struct ast_channel *chan, const char *data)
RemoveQueueMember application.
Definition: app_queue.c:8283
int stasis_message_router_add(struct stasis_message_router *router, struct stasis_message_type *message_type, stasis_subscription_cb callback, void *data)
Add a route to a message router.
static int upqm_exec(struct ast_channel *chan, const char *data)
UnpauseQueueMember application.
Definition: app_queue.c:8247
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1493
struct stasis_topic * ast_queue_topic_all(void)
Get the Stasis Message Bus API topic for queue messages.
Definition: main/app.c:3344
int stasis_subscription_set_filter(struct stasis_subscription *subscription, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a subscription.
Definition: stasis.c:1077
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
static struct stasis_topic * manager_topic
A stasis_topic that all topics AMI cares about will be forwarded to.
Definition: manager.c:1644
struct stasis_message_type * ast_device_state_message_type(void)
Get the Stasis message type for device state messages.
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
Definition: app_queue.c:1620
struct stasis_topic * ast_channel_topic_all(void)
A topic which publishes the events for all channels.
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3549
static int pqm_exec(struct ast_channel *chan, const char *data)
PauseQueueMember application.
Definition: app_queue.c:8211
static int reload_handler(int reload, struct ast_flags *mask, const char *queuename)
The command center for all reload operations.
Definition: app_queue.c:10003
struct stasis_message_type * ast_channel_agent_login_type(void)
Message type for agent login on a channel.
struct stasis_message_type * ast_channel_agent_logoff_type(void)
Message type for agent logoff on a channel.
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
static int ql_exec(struct ast_channel *chan, const char *data)
QueueLog application.
Definition: app_queue.c:8435
struct stasis_topic * ast_device_state_topic_all(void)
Get the Stasis topic for device state messages.
Definition: devicestate.c:668
static void device_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
set a member's status based on device state of that member's interface
Definition: app_queue.c:2641
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int manager_queues_status(struct mansession *s, const struct message *m)
Queue status info via AMI.
Definition: app_queue.c:10488
Structure used to handle boolean flags.
Definition: utils.h:199
struct stasis_topic * ast_manager_get_topic(void)
Get the Stasis Message Bus API topic for AMI.
Definition: manager.c:1880
static int manager_queues_summary(struct mansession *s, const struct message *m)
Summary of queue info via the AMI.
Definition: app_queue.c:10406
static int realtime_reason_paused
does realtime backend support reason_paused
Definition: app_queue.c:1623
static int aqm_exec(struct ast_channel *chan, const char *data)
AddQueueMember application.
Definition: app_queue.c:8354
int stasis_subscription_accept_message_type(struct stasis_subscription *subscription, const struct stasis_message_type *type)
Indicate to a subscription that we are interested in a message type.
Definition: stasis.c:1023
static int use_weight
Records that one or more queues use weight.
Definition: app_queue.c:1593
static int queue_exec(struct ast_channel *chan, const char *data)
The starting point for all queue calls.
Definition: app_queue.c:8512
static void reload_queue_members(void)
Reload dynamic queue members persisted into the astdb.
Definition: app_queue.c:8100
struct stasis_forward * stasis_forward_all(struct stasis_topic *from_topic, struct stasis_topic *to_topic)
Create a subscription which forwards all messages from one topic to another.
Definition: stasis.c:1578
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
static struct stasis_subscription * device_state_sub
Subscription to device state change messages.
Definition: app_queue.c:1608
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
static int queue_persistent_members
queues.conf [general] option
Definition: app_queue.c:1590
static int load_realtime_rules ( void  )
static

Load queue rules from realtime.

Check rule for errors with time or formatting, 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 3187 of file app_queue.c.

References ast_calloc, ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_copy_string(), AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_load_realtime_multientry(), queue_ent::max_penalty, penalty_rule::max_relative, penalty_rule::max_value, queue_ent::min_penalty, penalty_rule::min_relative, penalty_rule::min_value, queue_ent::raise_penalty, penalty_rule::raise_relative, penalty_rule::raise_value, and penalty_rule::time.

Referenced by reload_queue_rules().

3188 {
3189  struct ast_config *cfg;
3190  struct rule_list *rl_iter, *new_rl;
3191  struct penalty_rule *pr_iter;
3192  char *rulecat = NULL;
3193 
3194  if (!ast_check_realtime("queue_rules")) {
3195  ast_log(LOG_WARNING, "Missing \"queue_rules\" in extconfig.conf\n");
3196  return 0;
3197  }
3198  if (!(cfg = ast_load_realtime_multientry("queue_rules", "rule_name LIKE", "%", SENTINEL))) {
3199  ast_log(LOG_WARNING, "Failed to load queue rules from realtime\n");
3200  return 0;
3201  }
3202  while ((rulecat = ast_category_browse(cfg, rulecat))) {
3203  const char *timestr, *maxstr, *minstr, *raisestr, *rule_name;
3204  int penaltychangetime, rule_exists = 0, inserted = 0;
3205  int max_penalty = 0, min_penalty = 0, raise_penalty = 0;
3206  int min_relative = 0, max_relative = 0, raise_relative = 0;
3207  struct penalty_rule *new_penalty_rule = NULL;
3208 
3209  rule_name = ast_variable_retrieve(cfg, rulecat, "rule_name");
3210  if (ast_strlen_zero(rule_name)) {
3211  continue;
3212  }
3213 
3214  AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
3215  if (!(strcasecmp(rl_iter->name, rule_name))) {
3216  rule_exists = 1;
3217  new_rl = rl_iter;
3218  break;
3219  }
3220  }
3221  if (!rule_exists) {
3222  if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
3223  ast_config_destroy(cfg);
3224  return -1;
3225  }
3226  ast_copy_string(new_rl->name, rule_name, sizeof(new_rl->name));
3228  }
3229  timestr = ast_variable_retrieve(cfg, rulecat, "time");
3230  if (!(timestr) || sscanf(timestr, "%30d", &penaltychangetime) != 1) {
3231  ast_log(LOG_NOTICE, "Failed to parse time (%s) for one of the %s rules, skipping it\n",
3232  (ast_strlen_zero(timestr) ? "invalid value" : timestr), rule_name);
3233  continue;
3234  }
3235  if (!(new_penalty_rule = ast_calloc(1, sizeof(*new_penalty_rule)))) {
3236  ast_config_destroy(cfg);
3237  return -1;
3238  }
3239  if (!(maxstr = ast_variable_retrieve(cfg, rulecat, "max_penalty")) ||
3240  ast_strlen_zero(maxstr) || sscanf(maxstr, "%30d", &max_penalty) != 1) {
3241  max_penalty = 0;
3242  max_relative = 1;
3243  } else {
3244  if (*maxstr == '+' || *maxstr == '-') {
3245  max_relative = 1;
3246  }
3247  }
3248  if (!(minstr = ast_variable_retrieve(cfg, rulecat, "min_penalty")) ||
3249  ast_strlen_zero(minstr) || sscanf(minstr, "%30d", &min_penalty) != 1) {
3250  min_penalty = 0;
3251  min_relative = 1;
3252  } else {
3253  if (*minstr == '+' || *minstr == '-') {
3254  min_relative = 1;
3255  }
3256  }
3257  if (!(raisestr = ast_variable_retrieve(cfg, rulecat, "raise_penalty")) ||
3258  ast_strlen_zero(raisestr) || sscanf(raisestr, "%30d", &raise_penalty) != 1) {
3259  raise_penalty = 0;
3260  raise_relative = 1;
3261  } else {
3262  if (*raisestr == '+' || *raisestr == '-') {
3263  raise_relative = 1;
3264  }
3265  }
3266  new_penalty_rule->time = penaltychangetime;
3267  new_penalty_rule->max_relative = max_relative;
3268  new_penalty_rule->max_value = max_penalty;
3269  new_penalty_rule->min_relative = min_relative;
3270  new_penalty_rule->min_value = min_penalty;
3271  new_penalty_rule->raise_relative = raise_relative;
3272  new_penalty_rule->raise_value = raise_penalty;
3273  AST_LIST_TRAVERSE_SAFE_BEGIN(&new_rl->rules, pr_iter, list) {
3274  if (new_penalty_rule->time < pr_iter->time) {
3275  AST_LIST_INSERT_BEFORE_CURRENT(new_penalty_rule, list);
3276  inserted = 1;
3277  }
3278  }
3280  if (!inserted) {
3281  AST_LIST_INSERT_TAIL(&new_rl->rules, new_penalty_rule, list);
3282  }
3283  }
3284 
3285  ast_config_destroy(cfg);
3286  return 0;
3287 }
int max_relative
Definition: app_queue.c:1780
int min_relative
Definition: app_queue.c:1781
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3530
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
struct penalty_rule::@52 list
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
int raise_relative
Definition: app_queue.c:1782
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
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 counting the number of available members
Returns
Return the number of available members in queue q

Definition at line 4509 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, call_queue::autofill, and call_queue::members.

Referenced by is_our_turn(), and remove_from_queue().

4510 {
4511  struct member *mem;
4512  int avl = 0;
4513  struct ao2_iterator mem_iter;
4514 
4515  mem_iter = ao2_iterator_init(q->members, 0);
4516  while ((mem = ao2_iterator_next(&mem_iter))) {
4517 
4518  avl += is_member_available(q, mem);
4519  ao2_ref(mem, -1);
4520 
4521  /* If autofill is not enabled or if the queue's strategy is ringall, then
4522  * we really don't care about the number of available members so much as we
4523  * do that there is at least one available.
4524  *
4525  * In fact, we purposely will return from this function stating that only
4526  * one member is available if either of those conditions hold. That way,
4527  * functions which determine what action to take based on the number of available
4528  * members will operate properly. The reasoning is that even if multiple
4529  * members are available, only the head caller can actually be serviced.
4530  */
4531  if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
4532  break;
4533  }
4534  }
4535  ao2_iterator_destroy(&mem_iter);
4536 
4537  return avl;
4538 }
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ao2_container * members
Definition: app_queue.c:1887
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int autofill
Definition: app_queue.c:1883
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
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 8512 of file app_queue.c.

References call_queue::announce_to_first_user, call_queue::announcefrequency, ao2_container_count(), AST_APP_ARG, ast_app_exec_sub(), ast_app_parse_options(), AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_FIRST, ast_moh_start(), ast_moh_stop(), ast_party_id_presentation(), ast_replace_subargument_delimiter(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, queue_ent::chan, copy_rules(), queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), 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::log_restricted_caller_id, queue_ent::max_penalty, call_queue::members, queue_ent::min_penalty, queue_ent::moh, call_queue::name, queue_ent::opos, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), call_queue::periodicannouncefrequency, call_queue::periodicannouncestartdelay, queue_ent::pos, queue_ent::pr, queue_ent::predial_callee, queue_ent::prio, queue_ent::qe_rules, queue_ent::raise_penalty, record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), set_queue_result(), set_queue_variables(), queue_ent::start, stop, penalty_rule::time, update_qe_rule(), queue_ent::valid_digits, wait_our_turn(), queue_ent::withdraw, and queue_ent::withdraw_info.

Referenced by load_module().

8513 {
8514  int res=-1;
8515  int ringing=0;
8516  const char *user_priority;
8517  const char *max_penalty_str;
8518  const char *min_penalty_str;
8519  const char *raise_penalty_str;
8520  int prio;
8521  int qcontinue = 0;
8522  int max_penalty, min_penalty, raise_penalty;
8523  enum queue_result reason = QUEUE_UNKNOWN;
8524  /* whether to exit Queue application after the timeout hits */
8525  int tries = 0;
8526  int noption = 0;
8527  char *parse;
8528  int makeannouncement = 0;
8529  int position = 0;
8530  AST_DECLARE_APP_ARGS(args,
8531  AST_APP_ARG(queuename);
8532  AST_APP_ARG(options);
8533  AST_APP_ARG(url);
8534  AST_APP_ARG(announceoverride);
8535  AST_APP_ARG(queuetimeoutstr);
8536  AST_APP_ARG(agi);
8537  AST_APP_ARG(gosub);
8538  AST_APP_ARG(rule);
8539  AST_APP_ARG(position);
8540  );
8541  /* Our queue entry */
8542  struct queue_ent qe = { 0 };
8543  struct ast_flags opts = { 0, };
8544  char *opt_args[OPT_ARG_ARRAY_SIZE];
8545  int max_forwards;
8546  int cid_allow;
8547 
8548  if (ast_strlen_zero(data)) {
8549  ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,gosub[,rule[,position]]]]]]]]\n");
8550  return -1;
8551  }
8552 
8553  ast_channel_lock(chan);
8554  max_forwards = ast_max_forwards_get(chan);
8555  ast_channel_unlock(chan);
8556 
8557  if (max_forwards <= 0) {
8558  ast_log(LOG_WARNING, "Channel '%s' cannot enter queue. Max forwards exceeded\n", ast_channel_name(chan));
8559  return -1;
8560  }
8561 
8562  parse = ast_strdupa(data);
8563  AST_STANDARD_APP_ARGS(args, parse);
8564 
8565  ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, timeout: %s, agi: %s, gosub: %s, rule: %s, position: %s\n",
8566  args.queuename,
8567  S_OR(args.options, ""),
8568  S_OR(args.url, ""),
8569  S_OR(args.announceoverride, ""),
8570  S_OR(args.queuetimeoutstr, ""),
8571  S_OR(args.agi, ""),
8572  S_OR(args.gosub, ""),
8573  S_OR(args.rule, ""),
8574  S_OR(args.position, ""));
8575 
8576  if (!ast_strlen_zero(args.options)) {
8577  ast_app_parse_options(queue_exec_options, &opts, opt_args, args.options);
8578  }
8579 
8580  /* Setup our queue entry */
8581  qe.start = time(NULL);
8582 
8583  pbx_builtin_setvar_helper(chan, "ABANDONED", NULL);
8584 
8585  /* set the expire time based on the supplied timeout; */
8586  if (!ast_strlen_zero(args.queuetimeoutstr)) {
8587  qe.expire = qe.start + atoi(args.queuetimeoutstr);
8588  } else {
8589  qe.expire = 0;
8590  }
8591 
8592  /* Get the priority from the variable ${QUEUE_PRIO} */
8593  ast_channel_lock(chan);
8594  user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
8595  if (user_priority) {
8596  if (sscanf(user_priority, "%30d", &prio) == 1) {
8597  ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
8598  } else {
8599  ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
8600  user_priority, ast_channel_name(chan));
8601  prio = 0;
8602  }
8603  } else {
8604  ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
8605  prio = 0;
8606  }
8607 
8608  /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
8609 
8610  if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
8611  if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
8612  ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
8613  } else {
8614  ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
8615  max_penalty_str, ast_channel_name(chan));
8616  max_penalty = INT_MAX;
8617  }
8618  } else {
8619  max_penalty = INT_MAX;
8620  }
8621 
8622  if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
8623  if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
8624  ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
8625  } else {
8626  ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
8627  min_penalty_str, ast_channel_name(chan));
8628  min_penalty = INT_MAX;
8629  }
8630  } else {
8631  min_penalty = INT_MAX;
8632  }
8633 
8634  if ((raise_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_RAISE_PENALTY"))) {
8635  if (sscanf(raise_penalty_str, "%30d", &raise_penalty) == 1) {
8636  ast_debug(1, "%s: Got raise penalty %d from ${QUEUE_RAISE_PENALTY}.\n", ast_channel_name(chan), raise_penalty);
8637  } else {
8638  ast_log(LOG_WARNING, "${QUEUE_RAISE_PENALTY}: Invalid value (%s), channel %s.\n",
8639  raise_penalty_str, ast_channel_name(chan));
8640  raise_penalty = INT_MAX;
8641  }
8642  } else {
8643  raise_penalty = INT_MAX;
8644  }
8645  ast_channel_unlock(chan);
8646 
8647  if (ast_test_flag(&opts, OPT_RINGING)) {
8648  ringing = 1;
8649  }
8650 
8651  if (ringing != 1 && ast_test_flag(&opts, OPT_RING_WHEN_RINGING)) {
8652  qe.ring_when_ringing = 1;
8653  }
8654 
8655  if (ast_test_flag(&opts, OPT_GO_ON)) {
8656  qcontinue = 1;
8657  }
8658 
8659  if (args.position) {
8660  position = atoi(args.position);
8661  if (position < 0) {
8662  ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
8663  position = 0;
8664  }
8665  }
8666 
8667  ast_debug(1, "queue: %s, expires: %ld, priority: %d\n",
8668  args.queuename, (long)qe.expire, prio);
8669 
8670  qe.chan = chan;
8671  qe.prio = prio;
8672  qe.max_penalty = max_penalty;
8673  qe.min_penalty = min_penalty;
8674  qe.raise_penalty = raise_penalty;
8675  qe.last_pos_said = 0;
8676  qe.last_pos = 0;
8677  qe.last_periodic_announce_time = time(NULL);
8679  qe.valid_digits = 0;
8680  if (join_queue(args.queuename, &qe, &reason, position)) {
8681  ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
8682  set_queue_result(chan, reason);
8683  return 0;
8684  }
8685  ast_assert(qe.parent != NULL);
8686 
8687  if (qe.parent->periodicannouncestartdelay >= 0) {
8690  }
8691 
8692  cid_allow = qe.parent->log_restricted_caller_id || ((ast_party_id_presentation(&ast_channel_caller(chan)->id) & AST_PRES_RESTRICTION) == AST_PRES_ALLOWED);
8693 
8694  ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
8695  S_OR(args.url, ""),
8696  S_COR(cid_allow && ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, ""),
8697  qe.opos);
8698 
8699  /* PREDIAL: Preprocess any callee gosub arguments. */
8700  if (ast_test_flag(&opts, OPT_PREDIAL_CALLEE)
8701  && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLEE])) {
8702  ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLEE]);
8703  qe.predial_callee = opt_args[OPT_ARG_PREDIAL_CALLEE];
8704  }
8705 
8706  /* PREDIAL: Run gosub on the caller's channel */
8707  if (ast_test_flag(&opts, OPT_PREDIAL_CALLER)
8708  && !ast_strlen_zero(opt_args[OPT_ARG_PREDIAL_CALLER])) {
8709  ast_replace_subargument_delimiter(opt_args[OPT_ARG_PREDIAL_CALLER]);
8710  ast_app_exec_sub(NULL, chan, opt_args[OPT_ARG_PREDIAL_CALLER], 0);
8711  }
8712 
8713  /* Music on hold class override */
8714  if (ast_test_flag(&opts, OPT_MUSICONHOLD_CLASS)
8715  && !ast_strlen_zero(opt_args[OPT_ARG_MUSICONHOLD_CLASS])) {
8716  ast_copy_string(qe.moh, opt_args[OPT_ARG_MUSICONHOLD_CLASS], sizeof(qe.moh));
8717  }
8718 
8719  copy_rules(&qe, args.rule);
8720  qe.pr = AST_LIST_FIRST(&qe.qe_rules);
8721 check_turns:
8722  if (ringing) {
8724  } else {
8725  ast_moh_start(chan, qe.moh, NULL);
8726  }
8727 
8728  /* This is the wait loop for callers 2 through maxlen */
8729  res = wait_our_turn(&qe, ringing, &reason);
8730  if (res) {
8731  goto stop;
8732  }
8733 
8734  makeannouncement = qe.parent->announce_to_first_user;
8735 
8736  for (;;) {
8737  /* This is the wait loop for the head caller*/
8738  /* To exit, they may get their call answered; */
8739  /* they may dial a digit from the queue context; */
8740  /* or, they may timeout. */
8741 
8742  /* A request to withdraw this call from the queue arrived */
8743  if (qe.withdraw) {
8744  reason = QUEUE_WITHDRAW;
8745  res = 1;
8746  break;
8747  }
8748 
8749  /* Leave if we have exceeded our queuetimeout */
8750  if (qe.expire && (time(NULL) >= qe.expire)) {
8751  record_abandoned(&qe);
8752  reason = QUEUE_TIMEOUT;
8753  res = 0;
8754  ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld",
8755  qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8756  break;
8757  }
8758 
8759  if (makeannouncement) {
8760  /* Make a position announcement, if enabled */
8761  if (qe.parent->announcefrequency) {
8762  if ((res = say_position(&qe, ringing))) {
8763  goto stop;
8764  }
8765  }
8766  }
8767  makeannouncement = 1;
8768 
8769  /* Make a periodic announcement, if enabled */
8771  if ((res = say_periodic_announcement(&qe, ringing))) {
8772  goto stop;
8773  }
8774  }
8775 
8776  /* A request to withdraw this call from the queue arrived */
8777  if (qe.withdraw) {
8778  reason = QUEUE_WITHDRAW;
8779  res = 1;
8780  break;
8781  }
8782 
8783  /* Leave if we have exceeded our queuetimeout */
8784  if (qe.expire && (time(NULL) >= qe.expire)) {
8785  record_abandoned(&qe);
8786  reason = QUEUE_TIMEOUT;
8787  res = 0;
8788  ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
8789  "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8790  break;
8791  }
8792 
8793  /* see if we need to move to the next penalty level for this queue */
8794  while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
8795  update_qe_rule(&qe);
8796  }
8797 
8798  /* Try calling all queue members for 'timeout' seconds */
8799  res = try_calling(&qe, opts, opt_args, args.announceoverride, args.url, &tries, &noption, args.agi, args.gosub, ringing);
8800  if (res) {
8801  goto stop;
8802  }
8803 
8804  if (qe.parent->leavewhenempty) {
8805  int status = 0;
8806  if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.raise_penalty, qe.parent->leavewhenempty, 0))) {
8807  record_abandoned(&qe);
8808  reason = QUEUE_LEAVEEMPTY;
8809  ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
8810  res = 0;
8811  break;
8812  }
8813  }
8814 
8815  /* exit after 'timeout' cycle if 'n' option enabled */
8816  if (noption && tries >= ao2_container_count(qe.parent->members)) {
8817  ast_verb(3, "Exiting on time-out cycle\n");
8818  ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT",
8819  "%d|%d|%ld", qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8820  record_abandoned(&qe);
8821  reason = QUEUE_TIMEOUT;
8822  res = 0;
8823  break;
8824  }
8825 
8826 
8827  /* Leave if we have exceeded our queuetimeout */
8828  if (qe.expire && (time(NULL) >= qe.expire)) {
8829  record_abandoned(&qe);
8830  reason = QUEUE_TIMEOUT;
8831  res = 0;
8832  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));
8833  break;
8834  }
8835 
8836  /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
8837  res = wait_a_bit(&qe);
8838  if (res) {
8839  goto stop;
8840  }
8841 
8842  /* If using dynamic realtime members, we should regenerate the member list for this queue */
8843  update_realtime_members(qe.parent);
8844 
8845  /* Since this is a priority queue and
8846  * it is not sure that we are still at the head
8847  * of the queue, go and check for our turn again.
8848  */
8849  if (!is_our_turn(&qe)) {
8850  ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
8851  goto check_turns;
8852  }
8853  }
8854 
8855 stop:
8856  if (res) {
8857  if (reason == QUEUE_WITHDRAW) {
8858  record_abandoned(&qe);
8859  ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan), "NONE", "WITHDRAW", "%d|%d|%ld|%.40s", qe.pos, qe.opos, (long) (time(NULL) - qe.start), qe.withdraw_info ? qe.withdraw_info : "");
8860  if (qe.withdraw_info) {
8861  pbx_builtin_setvar_helper(qe.chan, "QUEUE_WITHDRAW_INFO", qe.withdraw_info);
8862  }
8863  res = 0;
8864  } else if (res < 0) {
8865  if (!qe.handled) {
8866  record_abandoned(&qe);
8867  ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
8868  "%d|%d|%ld", qe.pos, qe.opos,
8869  (long) (time(NULL) - qe.start));
8870  res = -1;
8871  } else if (reason == QUEUE_LEAVEEMPTY) {
8872  /* Return back to dialplan, don't hang up */
8873  res = 0;
8874  } else if (qcontinue) {
8875  reason = QUEUE_CONTINUE;
8876  res = 0;
8877  }
8878  } else if (qe.valid_digits) {
8879  ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
8880  "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) (time(NULL) - qe.start));
8881  }
8882  }
8883 
8884  /* Free the optional withdraw info if present */
8885  /* This is done here to catch all cases. e.g. if the call eventually wasn't withdrawn, e.g. answered */
8886  if (qe.withdraw_info) {
8887  ast_free(qe.withdraw_info);
8888  qe.withdraw_info = NULL;
8889  }
8890 
8891  /* Don't allow return code > 0 */
8892  if (res >= 0) {
8893  res = 0;
8894  if (ringing) {
8895  ast_indicate(chan, -1);
8896  } else {
8897  ast_moh_stop(chan);
8898  }
8899  ast_stopstream(chan);
8900  }
8901 
8903 
8904  leave_queue(&qe);
8905  if (reason != QUEUE_UNKNOWN)
8906  set_queue_result(chan, reason);
8907 
8908  /*
8909  * every queue_ent is given a reference to it's parent
8910  * call_queue when it joins the queue. This ref must be taken
8911  * away right before the queue_ent is destroyed. In this case
8912  * the queue_ent is about to be returned on the stack
8913  */
8914  qe.parent = queue_unref(qe.parent);
8915 
8916  return res;
8917 }
struct call_queue * parent
Definition: app_queue.c:1693
int raise_penalty
Definition: app_queue.c:1712
#define AST_LIST_FIRST(head)
Returns the first entry contained in a list.
Definition: linkedlists.h:421
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
char digits[AST_MAX_EXTENSION]
Definition: app_queue.c:1697
struct ast_channel * chan
Definition: app_queue.c:1720
int min_penalty
Definition: app_queue.c:1711
int max_penalty
Definition: app_queue.c:1710
int last_periodic_announce_sound
Definition: app_queue.c:1705
time_t start
Definition: app_queue.c:1715
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
int last_pos_said
Definition: app_queue.c:1702
int ast_party_id_presentation(const struct ast_party_id *id)
Determine the overall presentation value for the given party.
Definition: channel.c:1821
time_t expire
Definition: app_queue.c:1716
int periodicannouncestartdelay
Definition: app_queue.c:1855
int log_restricted_caller_id
Definition: app_queue.c:1885
static void set_queue_result(struct ast_channel *chan, enum queue_result res)
sets the QUEUESTATUS channel variable
Definition: app_queue.c:1910
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
Definition: app_queue.c:5018
unsigned int announce_to_first_user
Definition: app_queue.c:1834
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
struct ao2_container * members
Definition: app_queue.c:1887
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Number structure.
Definition: app_followme.c:154
void ast_replace_subargument_delimiter(char *s)
Replace '^' in a string with ','.
Definition: utils.c:2343
const char * predial_callee
Definition: app_queue.c:1698
struct queue_ent::@51 qe_rules
int periodicannouncefrequency
Definition: app_queue.c:1856
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
int ring_when_ringing
Definition: app_queue.c:1703
static void ringing(struct ast_channel *chan)
Helper method to send a ringing indication to a channel in a bridge.
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
int valid_digits
Definition: app_queue.c:1699
#define ast_debug(level,...)
Log a DEBUG message.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
char moh[MAX_MUSICCLASS]
Definition: app_queue.c:1694
static void leave_queue(struct queue_ent *qe)
Caller leaving queue.
Definition: app_queue.c:4372
const ast_string_field name
Definition: app_queue.c:1829
Channel datastore data for max forwards.
Definition: max_forwards.c:29
int ast_moh_start(struct ast_channel *chan, const char *mclass, const char *interpclass)
Turn on music on hold on a given channel.
Definition: channel.c:7766
time_t last_pos
Definition: app_queue.c:1706
unsigned int stop
Definition: res_smdi.c:217
struct penalty_rule * pr
Definition: app_queue.c:1722
Structure used to handle boolean flags.
Definition: utils.h:199
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.
Definition: app_queue.c:5884
unsigned int withdraw
Definition: app_queue.c:1718
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
Definition: app_queue.c:5079
time_t last_periodic_announce_time
Definition: app_queue.c:1704
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
Check if members are available.
Definition: app_queue.c:2422
char * withdraw_info
Definition: app_queue.c:1719
static void set_queue_variables(struct call_queue *q, struct ast_channel *chan)
Set variables of queue.
Definition: app_queue.c:2063
int ast_app_exec_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const char *sub_args, int ignore_hangup)
Run a subroutine on a channel, placing an optional second channel into autoservice.
Definition: main/app.c:297
int handled
Definition: app_queue.c:1708
static void copy_rules(struct queue_ent *qe, const char *rulename)
Copy rule from global list into specified queue.
Definition: app_queue.c:8469
static void update_qe_rule(struct queue_ent *qe)
update rules for queues
Definition: app_queue.c:5794
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
Definition: app_queue.c:5745
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
int announcefrequency
Definition: app_queue.c:1853
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define AST_APP_ARG(name)
Define an application argument.
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 9010 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_STANDARD_APP_ARGS, find_load_queue_rt_friendly(), get_wrapuptime(), member::lastcall, call_queue::members, member::paused, member::penalty, member::ringinuse, and member::status.

9011 {
9012  int count = 0;
9013  struct member *m;
9014  struct ao2_iterator mem_iter;
9015  struct call_queue *q;
9016 
9017  AST_DECLARE_APP_ARGS(args,
9018  AST_APP_ARG(queuename);
9019  AST_APP_ARG(option);
9020  AST_APP_ARG(interface);
9021  );
9022  /* Make sure the returned value on error is zero length string. */
9023  buf[0] = '\0';
9024 
9025  if (ast_strlen_zero(data)) {
9026  ast_log(LOG_ERROR,
9027  "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9028  cmd);
9029  return -1;
9030  }
9031 
9032  AST_STANDARD_APP_ARGS(args, data);
9033 
9034  if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.option)) {
9035  ast_log(LOG_ERROR,
9036  "Missing required argument. %s(<queuename>,<option>[,<interface>])\n",
9037  cmd);
9038  return -1;
9039  }
9040 
9041  if ((q = find_load_queue_rt_friendly(args.queuename))) {
9042  ao2_lock(q);
9043  if (!strcasecmp(args.option, "logged")) {
9044  mem_iter = ao2_iterator_init(q->members, 0);
9045  while ((m = ao2_iterator_next(&mem_iter))) {
9046  /* Count the agents who are logged in and presently answering calls */
9047  if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9048  count++;
9049  }
9050  ao2_ref(m, -1);
9051  }
9052  ao2_iterator_destroy(&mem_iter);
9053  } else if (!strcasecmp(args.option, "free")) {
9054  mem_iter = ao2_iterator_init(q->members, 0);
9055  while ((m = ao2_iterator_next(&mem_iter))) {
9056  /* Count the agents who are logged in and presently answering calls */
9057  if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
9058  count++;
9059  }
9060  ao2_ref(m, -1);
9061  }
9062  ao2_iterator_destroy(&mem_iter);
9063  } else if (!strcasecmp(args.option, "ready")) {
9064  time_t now;
9065  time(&now);
9066  mem_iter = ao2_iterator_init(q->members, 0);
9067  while ((m = ao2_iterator_next(&mem_iter))) {
9068  /* Count the agents who are logged in, not paused and not wrapping up */
9069  if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
9070  !(m->lastcall && get_wrapuptime(q, m) && ((now - get_wrapuptime(q, m)) < m->lastcall))) {
9071  count++;
9072  }
9073  ao2_ref(m, -1);
9074  }
9075  ao2_iterator_destroy(&mem_iter);
9076  } else if (!strcasecmp(args.option, "count")) {
9077  count = ao2_container_count(q->members);
9078  } else if (!strcasecmp(args.option, "penalty")) {
9079  m = get_interface_helper(q, args.interface);
9080  if (m) {
9081  count = m->penalty;
9082  ao2_ref(m, -1);
9083  }
9084  } else if (!strcasecmp(args.option, "paused")) {
9085  m = get_interface_helper(q, args.interface);
9086  if (m) {
9087  count = m->paused;
9088  ao2_ref(m, -1);
9089  }
9090  } else if ((!strcasecmp(args.option, "ignorebusy") /* ignorebusy is legacy */
9091  || !strcasecmp(args.option, "ringinuse"))) {
9092  m = get_interface_helper(q, args.interface);
9093  if (m) {
9094  count = m->ringinuse;
9095  ao2_ref(m, -1);
9096  }
9097  } else {
9098  ast_log(LOG_ERROR, "%s: Invalid option '%s' provided.\n", cmd, args.option);
9099  }
9100  ao2_unlock(q);
9101  queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
9102  } else {
9103  ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
9104  }
9105 
9106  snprintf(buf, len, "%d", count);
9107 
9108  return 0;
9109 }
int paused
Definition: app_queue.c:1738
int ao2_container_count(struct ao2_container *c)
Returns the number of elements in a container.
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ao2_container * members
Definition: app_queue.c:1887
int penalty
Definition: app_queue.c:1733
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
static int get_wrapuptime(struct call_queue *q, struct member *member)
Return wrapuptime.
Definition: app_queue.c:1991
int status
Definition: app_queue.c:1737
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
unsigned int ringinuse
Definition: app_queue.c:1751
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
time_t lastcall
Definition: app_queue.c:1744
#define AST_APP_ARG(name)
Define an application argument.
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 9175 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, find_load_queue_rt_friendly(), call_queue::members, and member::status.

9176 {
9177  int count = 0;
9178  struct member *m;
9179  struct call_queue *q;
9180  struct ao2_iterator mem_iter;
9181  static int depflag = 1;
9182 
9183  if (depflag) {
9184  depflag = 0;
9185  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");
9186  }
9187 
9188  if (ast_strlen_zero(data)) {
9189  ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
9190  return -1;
9191  }
9192 
9193  if ((q = find_load_queue_rt_friendly(data))) {
9194  ao2_lock(q);
9195  mem_iter = ao2_iterator_init(q->members, 0);
9196  while ((m = ao2_iterator_next(&mem_iter))) {
9197  /* Count the agents who are logged in and presently answering calls */
9198  if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
9199  count++;
9200  }
9201  ao2_ref(m, -1);
9202  }
9203  ao2_iterator_destroy(&mem_iter);
9204  ao2_unlock(q);
9205  queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
9206  } else {
9207  ast_log(LOG_WARNING, "queue %s was not found\n", data);
9208  }
9209 
9210  snprintf(buf, len, "%d", count);
9211 
9212  return 0;
9213 }
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ao2_container * members
Definition: app_queue.c:1887
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int status
Definition: app_queue.c:1737
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
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 8924 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, find_load_queue_rt_friendly(), call_queue::holdtime, call_queue::maxlen, pbx_builtin_setvar_multiple(), call_queue::servicelevel, and call_queue::talktime.

8925 {
8926  int res = -1;
8927  struct call_queue *q;
8928  char interfacevar[256] = "";
8929  float sl = 0;
8930 
8931  if (ast_strlen_zero(data)) {
8932  ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
8933  return -1;
8934  }
8935 
8936  if ((q = find_load_queue_rt_friendly(data))) {
8937  ao2_lock(q);
8938  if (q->setqueuevar) {
8939  sl = 0;
8940  res = 0;
8941 
8942  if (q->callscompleted > 0) {
8943  sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
8944  }
8945 
8946  snprintf(interfacevar, sizeof(interfacevar),
8947  "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
8948  q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl);
8949 
8950  pbx_builtin_setvar_multiple(chan, interfacevar);
8951  }
8952 
8953  ao2_unlock(q);
8954  queue_t_unref(q, "Done with QUEUE() function");
8955  } else {
8956  ast_log(LOG_WARNING, "queue %s was not found\n", data);
8957  }
8958 
8959  snprintf(buf, len, "%d", res);
8960 
8961  return 0;
8962 }
int servicelevel
Definition: app_queue.c:1865
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
int callscompletedinsl
Definition: app_queue.c:1866
int holdtime
Definition: app_queue.c:1860
int pbx_builtin_setvar_multiple(struct ast_channel *chan, const char *data)
Parse and set multiple channel variables, where the pairs are separated by the ',' character...
int talktime
Definition: app_queue.c:1861
int callsabandoned
Definition: app_queue.c:1863
int callscompleted
Definition: app_queue.c:1862
static void queue_reset_global_params ( void  )
static

Always set the global queue defaults, even if there is no "general" section in queues.conf

Definition at line 9570 of file app_queue.c.

Referenced by reload_queues().

9571 {
9573  autofill_default = 0;
9574  montype_default = 0;
9575  shared_lastcall = 0;
9579 }
static int force_longest_waiting_caller
queues.conf [general] option
Definition: app_queue.c:1617
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1611
static int log_membername_as_agent
queues.conf [general] option
Definition: app_queue.c:1614
static int montype_default
queues.conf [general] option
Definition: app_queue.c:1599
static int autofill_default
queues.conf [general] option
Definition: app_queue.c:1596
static int shared_lastcall
queues.conf [general] option
Definition: app_queue.c:1602
static int queue_persistent_members
queues.conf [general] option
Definition: app_queue.c:1590
static void queue_rules_reset_global_params ( void  )
static

Reset the global queue rules parameters even if there is no "general" section of queuerules.conf

Definition at line 9491 of file app_queue.c.

Referenced by reload_queue_rules().

9492 {
9493  realtime_rules = 0;
9494 }
static int realtime_rules
queuerules.conf [general] option
Definition: app_queue.c:1605
static void queue_rules_set_global_params ( struct ast_config cfg)
static

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

Definition at line 9497 of file app_queue.c.

References ast_true().

Referenced by reload_queue_rules().

9498 {
9499  const char *general_val = NULL;
9500  if ((general_val = ast_variable_retrieve(cfg, "general", "realtime_rules"))) {
9501  realtime_rules = ast_true(general_val);
9502  }
9503 }
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
static int realtime_rules
queuerules.conf [general] option
Definition: app_queue.c:1605
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 9582 of file app_queue.c.

References ast_true().

Referenced by reload_queues().

9583 {
9584  const char *general_val = NULL;
9585  if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
9586  queue_persistent_members = ast_true(general_val);
9587  }
9588  if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
9589  autofill_default = ast_true(general_val);
9590  }
9591  if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
9592  if (!strcasecmp(general_val, "mixmonitor"))
9593  montype_default = 1;
9594  }
9595  if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
9596  shared_lastcall = ast_true(general_val);
9597  }
9598  if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
9599  negative_penalty_invalid = ast_true(general_val);
9600  }
9601  if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
9602  log_membername_as_agent = ast_true(general_val);
9603  }
9604  if ((general_val = ast_variable_retrieve(cfg, "general", "force_longest_waiting_caller"))) {
9605  force_longest_waiting_caller = ast_true(general_val);
9606  }
9607 }
static int force_longest_waiting_caller
queues.conf [general] option
Definition: app_queue.c:1617
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1611
static int log_membername_as_agent
queues.conf [general] option
Definition: app_queue.c:1614
static int montype_default
queues.conf [general] option
Definition: app_queue.c:1599
static int autofill_default
queues.conf [general] option
Definition: app_queue.c:1596
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
static int shared_lastcall
queues.conf [general] option
Definition: app_queue.c:1602
static int queue_persistent_members
queues.conf [general] option
Definition: app_queue.c:1590
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 3332 of file app_queue.c.

References call_queue::announce_to_first_user, call_queue::announcefrequency, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, call_queue::announceposition_only_up, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_str_create, ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, call_queue::autopausedelay, call_queue::log_restricted_caller_id, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::name, call_queue::numperiodicannounce, call_queue::penaltymemberslimit, call_queue::periodicannouncefrequency, call_queue::periodicannouncestartdelay, call_queue::randomperiodicannounce, call_queue::retry, call_queue::roundingseconds, call_queue::servicelevel, call_queue::sound_periodicannounce, call_queue::timeout, call_queue::timeoutpriority, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

3333 {
3334  if (!strcasecmp(param, "musicclass") ||
3335  !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
3337  } else if (!strcasecmp(param, "announce")) {
3339  } else if (!strcasecmp(param, "context")) {
3341  } else if (!strcasecmp(param, "timeout")) {
3342  q->timeout = atoi(val);
3343  if (q->timeout < 0) {
3344  q->timeout = DEFAULT_TIMEOUT;
3345  }
3346  } else if (!strcasecmp(param, "ringinuse")) {
3347  q->ringinuse = ast_true(val);
3348  } else if (!strcasecmp(param, "setinterfacevar")) {
3349  q->setinterfacevar = ast_true(val);
3350  } else if (!strcasecmp(param, "setqueuevar")) {
3351  q->setqueuevar = ast_true(val);
3352  } else if (!strcasecmp(param, "setqueueentryvar")) {
3353  q->setqueueentryvar = ast_true(val);
3354  } else if (!strcasecmp(param, "monitor-format")) {
3355  ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
3356  } else if (!strcasecmp(param, "membergosub")) {
3358  } else if (!strcasecmp(param, "queue-youarenext")) {
3360  } else if (!strcasecmp(param, "queue-thereare")) {
3362  } else if (!strcasecmp(param, "queue-callswaiting")) {
3364  } else if (!strcasecmp(param, "queue-quantity1")) {
3366  } else if (!strcasecmp(param, "queue-quantity2")) {
3368  } else if (!strcasecmp(param, "queue-holdtime")) {
3370  } else if (!strcasecmp(param, "queue-minutes")) {
3372  } else if (!strcasecmp(param, "queue-minute")) {
3374  } else if (!strcasecmp(param, "queue-seconds")) {
3376  } else if (!strcasecmp(param, "queue-thankyou")) {
3378  } else if (!strcasecmp(param, "queue-callerannounce")) {
3380  } else if (!strcasecmp(param, "queue-reporthold")) {
3382  } else if (!strcasecmp(param, "announce-frequency")) {
3383  q->announcefrequency = atoi(val);
3384  } else if (!strcasecmp(param, "announce-to-first-user")) {
3386  } else if (!strcasecmp(param, "min-announce-frequency")) {
3387  q->minannouncefrequency = atoi(val);
3388  ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
3389  } else if (!strcasecmp(param, "announce-round-seconds")) {
3390  q->roundingseconds = atoi(val);
3391  /* Rounding to any other values just doesn't make sense... */
3392  if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
3393  || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
3394  if (linenum >= 0) {
3395  ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3396  "using 0 instead for queue '%s' at line %d of queues.conf\n",
3397  val, param, q->name, linenum);
3398  } else {
3399  ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
3400  "using 0 instead for queue '%s'\n", val, param, q->name);
3401  }
3402  q->roundingseconds=0;
3403  }
3404  } else if (!strcasecmp(param, "announce-holdtime")) {
3405  if (!strcasecmp(val, "once")) {
3406  q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
3407  } else if (ast_true(val)) {
3408  q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
3409  } else {
3410  q->announceholdtime = 0;
3411  }
3412  } else if (!strcasecmp(param, "announce-position")) {
3413  if (!strcasecmp(val, "limit")) {
3414  q->announceposition = ANNOUNCEPOSITION_LIMIT;
3415  } else if (!strcasecmp(val, "more")) {
3416  q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
3417  } else if (ast_true(val)) {
3418  q->announceposition = ANNOUNCEPOSITION_YES;
3419  } else {
3420  q->announceposition = ANNOUNCEPOSITION_NO;
3421  }
3422  } else if (!strcasecmp(param, "announce-position-only-up")) {
3424  } else if (!strcasecmp(param, "announce-position-limit")) {
3425  q->announcepositionlimit = atoi(val);
3426  } else if (!strcasecmp(param, "periodic-announce")) {
3427  if (strchr(val, ',')) {
3428  char *s, *buf = ast_strdupa(val);
3429  unsigned int i = 0;
3430 
3431  while ((s = strsep(&buf, ",|"))) {
3432  if (!q->sound_periodicannounce[i]) {
3434  }
3435  ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
3436  i++;
3437  if (i == MAX_PERIODIC_ANNOUNCEMENTS) {
3438  break;
3439  }
3440  }
3441  q->numperiodicannounce = i;
3442  } else {
3443  ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
3444  q->numperiodicannounce = 1;
3445  }
3446  } else if (!strcasecmp(param, "periodic-announce-startdelay")) {
3447  q->periodicannouncestartdelay = atoi(val);
3448  } else if (!strcasecmp(param, "periodic-announce-frequency")) {
3449  q->periodicannouncefrequency = atoi(val);
3450  } else if (!strcasecmp(param, "relative-periodic-announce")) {
3451  q->relativeperiodicannounce = ast_true(val);
3452  } else if (!strcasecmp(param, "random-periodic-announce")) {
3454  } else if (!strcasecmp(param, "retry")) {
3455  q->retry = atoi(val);
3456  if (q->retry <= 0) {
3457  q->retry = DEFAULT_RETRY;
3458  }
3459  } else if (!strcasecmp(param, "wrapuptime")) {
3460  q->wrapuptime = atoi(val);
3461  } else if (!strcasecmp(param, "penaltymemberslimit")) {
3462  if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
3463  q->penaltymemberslimit = 0;
3464  }
3465  } else if (!strcasecmp(param, "autofill")) {
3466  q->autofill = ast_true(val);
3467  } else if (!strcasecmp(param, "autopause")) {
3468  q->autopause = autopause2int(val);
3469  } else if (!strcasecmp(param, "autopausedelay")) {
3470  q->autopausedelay = atoi(val);
3471  } else if (!strcasecmp(param, "autopausebusy")) {
3472  q->autopausebusy = ast_true(val);
3473  } else if (!strcasecmp(param, "autopauseunavail")) {
3474  q->autopauseunavail = ast_true(val);
3475  } else if (!strcasecmp(param, "maxlen")) {
3476  q->maxlen = atoi(val);
3477  if (q->maxlen < 0) {
3478  q->maxlen = 0;
3479  }
3480  } else if (!strcasecmp(param, "servicelevel")) {
3481  q->servicelevel= atoi(val);
3482  } else if (!strcasecmp(param, "strategy")) {
3483  int strategy;
3484 
3485  /* We are a static queue and already have set this, no need to do it again */
3486  if (failunknown) {
3487  return;
3488  }
3489  strategy = strat2int(val);
3490  if (strategy < 0) {
3491  ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
3492  val, q->name);
3493  strategy = QUEUE_STRATEGY_RINGALL;
3494  }
3495  if (strategy == q->strategy) {
3496  return;
3497  }
3498  if (strategy == QUEUE_STRATEGY_LINEAR) {
3499  ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
3500  return;
3501  }
3502  q->strategy = strategy;
3503  } else if (!strcasecmp(param, "joinempty")) {
3504  parse_empty_options(val, &q->joinempty, 1);
3505  } else if (!strcasecmp(param, "leavewhenempty")) {
3506  parse_empty_options(val, &q->leavewhenempty, 0);
3507  } else if (!strcasecmp(param, "reportholdtime")) {
3508  q->reportholdtime = ast_true(val);
3509  } else if (!strcasecmp(param, "memberdelay")) {
3510  q->memberdelay = atoi(val);
3511  } else if (!strcasecmp(param, "weight")) {
3512  q->weight = atoi(val);
3513  } else if (!strcasecmp(param, "timeoutrestart")) {
3514  q->timeoutrestart = ast_true(val);
3515  } else if (!strcasecmp(param, "defaultrule")) {
3517  } else if (!strcasecmp(param, "timeoutpriority")) {
3518  if (!strcasecmp(val, "conf")) {
3519  q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
3520  } else {
3521  q->timeoutpriority = TIMEOUT_PRIORITY_APP;
3522  }
3523  } else if (!strcasecmp(param, "log-restricted-caller-id")) {
3525  } else if (failunknown) {
3526  if (linenum >= 0) {
3527  ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
3528  q->name, param, linenum);
3529  } else {
3530  ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
3531  }
3532  }
3533 }
struct ast_str * sound_periodicannounce[MAX_PERIODIC_ANNOUNCEMENTS]
Definition: app_queue.c:1831
int servicelevel
Definition: app_queue.c:1865
#define MAX_PERIODIC_ANNOUNCEMENTS
Definition: app_queue.c:1556
const ast_string_field sound_holdtime
Definition: app_queue.c:1829
int wrapuptime
Definition: app_queue.c:1870
const ast_string_field membergosub
Definition: app_queue.c:1829
int penaltymemberslimit
Definition: app_queue.c:1871
#define ANNOUNCEPOSITION_NO
Definition: app_queue.c:1787
unsigned int announceposition_only_up
Definition: app_queue.c:1843
int minannouncefrequency
Definition: app_queue.c:1854
int autopause
Definition: app_queue.c:1876
int periodicannouncestartdelay
Definition: app_queue.c:1855
int log_restricted_caller_id
Definition: app_queue.c:1885
const ast_string_field sound_thanks
Definition: app_queue.c:1829
unsigned int announce_to_first_user
Definition: app_queue.c:1834
char monfmt[8]
Definition: app_queue.c:1867
int announcepositionlimit
Definition: app_queue.c:1852
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
const ast_string_field sound_minute
Definition: app_queue.c:1829
int periodicannouncefrequency
Definition: app_queue.c:1856
int memberdelay
Definition: app_queue.c:1882
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
const ast_string_field sound_thereare
Definition: app_queue.c:1829
const ast_string_field context
Definition: app_queue.c:1829
const ast_string_field sound_next
Definition: app_queue.c:1829
#define ast_debug(level,...)
Log a DEBUG message.
#define ANNOUNCEPOSITION_MORE_THAN
Definition: app_queue.c:1788
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
#define ANNOUNCEPOSITION_YES
Definition: app_queue.c:1786
int autopausedelay
Definition: app_queue.c:1877
const ast_string_field name
Definition: app_queue.c:1829
int autofill
Definition: app_queue.c:1883
int roundingseconds
Definition: app_queue.c:1859
const ast_string_field moh
Definition: app_queue.c:1829
int timeoutpriority
Definition: app_queue.c:1878
const ast_string_field defaultrule
Definition: app_queue.c:1829
const ast_string_field sound_seconds
Definition: app_queue.c:1829
int randomperiodicannounce
Definition: app_queue.c:1858
const ast_string_field announce
Definition: app_queue.c:1829
const ast_string_field sound_reporthold
Definition: app_queue.c:1829
#define ANNOUNCEPOSITION_LIMIT
Definition: app_queue.c:1789
const ast_string_field sound_calls
Definition: app_queue.c:1829
int numperiodicannounce
Definition: app_queue.c:1857
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
const ast_string_field sound_minutes
Definition: app_queue.c:1829
const ast_string_field queue_quantity2
Definition: app_queue.c:1829
const ast_string_field queue_quantity1
Definition: app_queue.c:1829
const ast_string_field sound_callerannounce
Definition: app_queue.c:1829
int announcefrequency
Definition: app_queue.c:1853
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
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 10003 of file app_queue.c.

References clear_stats(), reload_queue_rules(), and reload_queues().

Referenced by load_module().

10004 {
10005  int res = 0;
10006 
10007  if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
10008  res |= reload_queue_rules(reload);
10009  }
10010  if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
10011  res |= clear_stats(queuename);
10012  }
10013  if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
10014  res |= reload_queues(reload, mask, queuename);
10015  }
10016  return res;
10017 }
static int reload_queue_rules(int reload)
Reload the rules defined in queuerules.conf.
Definition: app_queue.c:9511
static int reload_queues(int reload, struct ast_flags *mask, const char *queuename)
reload the queues.conf file
Definition: app_queue.c:9916
static int clear_stats(const char *queuename)
Facilitates resetting statistics for a queue.
Definition: app_queue.c:9973
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 9511 of file app_queue.c.

References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, CONFIG_FLAG_FILEUNCHANGED, insert_penaltychange(), load_realtime_rules(), ast_variable::name, ast_variable::next, queue_rules_reset_global_params(), queue_rules_set_global_params(), realtime_rules, and ast_variable::value.

Referenced by reload_handler().

9512 {
9513  struct ast_config *cfg;
9514  struct rule_list *rl_iter, *new_rl;
9515  struct penalty_rule *pr_iter;
9516  char *rulecat = NULL;
9517  struct ast_variable *rulevar = NULL;
9518  struct ast_flags config_flags = { (reload && !realtime_rules) ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9519 
9520  if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
9521  ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
9522  return AST_MODULE_LOAD_SUCCESS;
9523  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9524  ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
9525  return AST_MODULE_LOAD_SUCCESS;
9526  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9527  ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n");
9528  return AST_MODULE_LOAD_SUCCESS;
9529  }
9530 
9532  while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
9533  while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
9534  ast_free(pr_iter);
9535  ast_free(rl_iter);
9536  }
9538  while ((rulecat = ast_category_browse(cfg, rulecat))) {
9539  if (!strcasecmp(rulecat, "general")) {
9541  continue;
9542  }
9543  if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
9545  ast_config_destroy(cfg);
9546  return AST_MODULE_LOAD_DECLINE;
9547  } else {
9548  ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
9549  AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
9550  for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
9551  if(!strcasecmp(rulevar->name, "penaltychange"))
9552  insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
9553  else
9554  ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
9555  }
9556  }
9557 
9558  ast_config_destroy(cfg);
9559 
9562  return AST_MODULE_LOAD_DECLINE;
9563  }
9564 
9566  return AST_MODULE_LOAD_SUCCESS;
9567 }
struct ast_variable * next
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
static void queue_rules_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9497
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
Structure for variables, used for configurations and for channel variables.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
static int insert_penaltychange(const char *list_name, const char *content, const int linenum)
Change queue penalty by adding rule.
Definition: app_queue.c:3083
#define ast_config_load(filename, flags)
Load a config file.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Structure used to handle boolean flags.
Definition: utils.h:199
static int realtime_rules
queuerules.conf [general] option
Definition: app_queue.c:1605
static void queue_rules_reset_global_params(void)
Definition: app_queue.c:9491
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
static int load_realtime_rules(void)
Load queue rules from realtime.
Definition: app_queue.c:3187
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 9916 of file app_queue.c.

References ao2_callback, ast_category_browse(), ast_config_destroy(), ast_config_load, CONFIG_FLAG_FILEUNCHANGED, OBJ_MULTIPLE, OBJ_NODATA, OBJ_NOLOCK, OBJ_UNLINK, queue_reset_global_params(), queue_set_global_params(), and reload_single_queue().

Referenced by reload_handler().

9917 {
9918  struct ast_config *cfg;
9919  char *cat;
9920  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
9921  const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
9922 
9923  if (!(cfg = ast_config_load("queues.conf", config_flags))) {
9924  ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
9925  return -1;
9926  } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
9927  return 0;
9928  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
9929  ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n");
9930  return -1;
9931  }
9932 
9933  /* We've made it here, so it looks like we're doing operations on all queues. */
9934  ao2_lock(queues);
9935 
9936  /* Mark non-realtime queues not found at the beginning. */
9937  ao2_callback(queues, OBJ_NODATA, mark_unfound, (char *) queuename);
9938 
9939  /* Chug through config file. */
9940  cat = NULL;
9942  while ((cat = ast_category_browse(cfg, cat)) ) {
9943  if (!strcasecmp(cat, "general") && queue_reload) {
9945  continue;
9946  }
9947  if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
9948  reload_single_queue(cfg, mask, cat);
9949  }
9950 
9951  ast_config_destroy(cfg);
9952  if (queue_reload) {
9953  /* Unlink and mark dead all non-realtime queues that were not found in the configuration file. */
9954  ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK | OBJ_NOLOCK, kill_if_unfound, (char *) queuename);
9955  }
9956  ao2_unlock(queues);
9957  return 0;
9958 }
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
static void queue_reset_global_params(void)
Definition: app_queue.c:9570
Assume that the ao2_container is already locked.
Definition: astobj2.h:1063
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
#define ast_config_load(filename, flags)
Load a config file.
static void queue_set_global_params(struct ast_config *cfg)
Definition: app_queue.c:9582
Structure used to handle boolean flags.
Definition: utils.h:199
static void reload_single_queue(struct ast_config *cfg, struct ast_flags *mask, const char *queuename)
Reload information pertaining to a particular queue.
Definition: app_queue.c:9759
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
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 9617 of file app_queue.c.

References ao2_link, ao2_ref, ao2_unlink, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_false(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strip(), ast_true(), member::calls, create_queue_member(), member::interface, member::lastcall, call_queue::members, call_queue::name, OBJ_POINTER, member::paused, member::penalty, member::queuepos, member::ringinuse, and member::wrapuptime.

Referenced by reload_single_queue().

9618 {
9619  char *membername, *interface, *state_interface, *tmp;
9620  char *parse;
9621  struct member *cur, *newm;
9622  struct member tmpmem;
9623  int penalty;
9624  int ringinuse;
9625  int wrapuptime;
9626  AST_DECLARE_APP_ARGS(args,
9627  AST_APP_ARG(interface);
9628  AST_APP_ARG(penalty);
9629  AST_APP_ARG(membername);
9630  AST_APP_ARG(state_interface);
9631  AST_APP_ARG(ringinuse);
9632  AST_APP_ARG(wrapuptime);
9633  );
9634 
9635  if (ast_strlen_zero(memberdata)) {
9636  ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
9637  return;
9638  }
9639 
9640  /* Add a new member */
9641  parse = ast_strdupa(memberdata);
9642 
9643  AST_STANDARD_APP_ARGS(args, parse);
9644 
9645  interface = args.interface;
9646  if (!ast_strlen_zero(args.penalty)) {
9647  tmp = args.penalty;
9648  ast_strip(tmp);
9649  penalty = atoi(tmp);
9650  if (penalty < 0) {
9651  penalty = 0;
9652  }
9653  } else {
9654  penalty = 0;
9655  }
9656 
9657  if (!ast_strlen_zero(args.membername)) {
9658  membername = args.membername;
9659  ast_strip(membername);
9660  } else {
9661  membername = interface;
9662  }
9663 
9664  if (!ast_strlen_zero(args.state_interface)) {
9665  state_interface = args.state_interface;
9666  ast_strip(state_interface);
9667  } else {
9668  state_interface = interface;
9669  }
9670 
9671  if (!ast_strlen_zero(args.ringinuse)) {
9672  tmp = args.ringinuse;
9673  ast_strip(tmp);
9674  if (ast_true(tmp)) {
9675  ringinuse = 1;
9676  } else if (ast_false(tmp)) {
9677  ringinuse = 0;
9678  } else {
9679  ast_log(LOG_ERROR, "Member %s has an invalid ringinuse value. Using %s ringinuse value.\n",
9680  membername, q->name);
9681  ringinuse = q->ringinuse;
9682  }
9683  } else {
9684  ringinuse = q->ringinuse;
9685  }
9686 
9687  if (!ast_strlen_zero(args.wrapuptime)) {
9688  tmp = args.wrapuptime;
9689  ast_strip(tmp);
9690  wrapuptime = atoi(tmp);
9691  if (wrapuptime < 0) {
9692  wrapuptime = 0;
9693  }
9694  } else {
9695  wrapuptime = 0;
9696  }
9697 
9698  /* Find the old position in the list */
9699  ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
9700  cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
9701 
9702  if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface, ringinuse, wrapuptime))) {
9703  newm->wrapuptime = wrapuptime;
9704  if (cur) {
9705  ao2_lock(q->members);
9706  /* Round Robin Queue Position must be copied if this is replacing an existing member */
9707  newm->queuepos = cur->queuepos;
9708  /* Don't reset agent stats either */
9709  newm->calls = cur->calls;
9710  newm->lastcall = cur->lastcall;
9711 
9712  ao2_link(q->members, newm);
9713  ao2_unlink(q->members, cur);
9714  ao2_unlock(q->members);
9715  } else {
9716  /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
9717  member_add_to_queue(q, newm);
9718  }
9719  ao2_ref(newm, -1);
9720  }
9721  newm = NULL;
9722 
9723  if (cur) {
9724  ao2_ref(cur, -1);
9725  }
9726 }
int queuepos
Definition: app_queue.c:1740
static struct member * create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
allocate space for new queue member and set fields based on parameters passed
Definition: app_queue.c:2865
int paused
Definition: app_queue.c:1738
int wrapuptime
Definition: app_queue.c:1742
#define OBJ_POINTER
Definition: astobj2.h:1150
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
struct ao2_container * members
Definition: app_queue.c:1887
int penalty
Definition: app_queue.c:1733
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition: strings.h:223
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
const ast_string_field name
Definition: app_queue.c:1829
int calls
Definition: app_queue.c:1734
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
unsigned int ringinuse
Definition: app_queue.c:1751
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
time_t lastcall
Definition: app_queue.c:1744
#define AST_APP_ARG(name)
Define an application argument.
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
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

Definition at line 9759 of file app_queue.c.

References ao2_callback, ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_ref, ao2_t_find, ast_atomic_fetchadd_int(), member::dynamic, init_queue(), call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_POINTER, OBJ_UNLINK, queue_set_param(), reload_single_member(), member::ringinuse, ast_variable::value, and call_queue::weight.

Referenced by reload_queues().

9760 {
9761  int new;
9762  struct call_queue *q = NULL;
9763  struct member *member;
9764  /*We're defining a queue*/
9765  struct call_queue tmpq = {
9766  .name = queuename,
9767  };
9768  const char *tmpvar;
9769  const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
9770  const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
9771  int prev_weight = 0;
9772  struct ast_variable *var;
9773  struct ao2_iterator mem_iter;
9774 
9775  if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
9776  if (queue_reload) {
9777  /* Make one then */
9778  if (!(q = alloc_queue(queuename))) {
9779  return;
9780  }
9781  } else {
9782  /* Since we're not reloading queues, this means that we found a queue
9783  * in the configuration file which we don't know about yet. Just return.
9784  */
9785  return;
9786  }
9787  new = 1;
9788  } else {
9789  new = 0;
9790  }
9791 
9792  if (!new) {
9793  ao2_lock(q);
9794  prev_weight = q->weight ? 1 : 0;
9795  }
9796  /* Check if we already found a queue with this name in the config file */
9797  if (q->found) {
9798  ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
9799  if (!new) {
9800  /* It should be impossible to *not* hit this case*/
9801  ao2_unlock(q);
9802  }
9803  queue_t_unref(q, "We exist! Expiring temporary pointer");
9804  return;
9805  }
9806  /* Due to the fact that the "linear" strategy will have a different allocation
9807  * scheme for queue members, we must devise the queue's strategy before other initializations.
9808  * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
9809  * container used will have only a single bucket instead of the typical number.
9810  */
9811  if (queue_reload) {
9812  if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
9813  q->strategy = strat2int(tmpvar);
9814  if (q->strategy < 0) {
9815  ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
9816  tmpvar, q->name);
9817  q->strategy = QUEUE_STRATEGY_RINGALL;
9818  }
9819  } else {
9820  q->strategy = QUEUE_STRATEGY_RINGALL;
9821  }
9822  init_queue(q);
9823  }
9824  if (member_reload) {
9825  ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
9826  q->found = 1;
9827  }
9828 
9829  /* On the first pass we just read the parameters of the queue */
9830  for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
9831  if (queue_reload && strcasecmp(var->name, "member")) {
9832  queue_set_param(q, var->name, var->value, var->lineno, 1);
9833  }
9834  }
9835 
9836  /* On the second pass, we read members */
9837  for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
9838  if (member_reload && !strcasecmp(var->name, "member")) {
9839  reload_single_member(var->value, q);
9840  }
9841  }
9842 
9843  /* Update ringinuse for dynamic members */
9844  if (member_reload) {
9845  ao2_lock(q->members);
9847  while ((member = ao2_iterator_next(&mem_iter))) {
9848  if (member->dynamic) {
9849  member->ringinuse = q->ringinuse;
9850  }
9851  ao2_ref(member, -1);
9852  }
9853  ao2_iterator_destroy(&mem_iter);
9854  ao2_unlock(q->members);
9855  }
9856 
9857  /* At this point, we've determined if the queue has a weight, so update use_weight
9858  * as appropriate
9859  */
9860  if (!q->weight && prev_weight) {
9862  } else if (q->weight && !prev_weight) {
9864  }
9865 
9866  /* Free remaining members marked as delme */
9867  if (member_reload) {
9868  ao2_lock(q->members);
9869  ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q);
9870  ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
9871  ao2_unlock(q->members);
9872  }
9873 
9874  if (new) {
9875  queues_t_link(queues, q, "Add queue to container");
9876  } else {
9877  ao2_unlock(q);
9878  }
9879  queue_t_unref(q, "Expiring creation reference");
9880 }
struct ast_variable * next
int dynamic
Definition: app_queue.c:1735
#define OBJ_POINTER
Definition: astobj2.h:1150
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
Structure for variables, used for configurations and for channel variables.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static void reload_single_member(const char *memberdata, struct call_queue *q)
reload information pertaining to a single member
Definition: app_queue.c:9617
struct ao2_container * members
Definition: app_queue.c:1887
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:757
static void queue_set_param(struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
Configure a queue parameter.
Definition: app_queue.c:3332
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
const ast_string_field name
Definition: app_queue.c:1829
Assume that the ao2_container is already locked.
Definition: astobj2.h:1852
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
#define ao2_t_find(container, arg, flags, tag)
Definition: astobj2.h:1734
static int use_weight
Records that one or more queues use weight.
Definition: app_queue.c:1593
static void init_queue(struct call_queue *q)
Initialize Queue default values.
Definition: app_queue.c:2949
unsigned int ringinuse
Definition: app_queue.c:1751
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
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 7540 of file app_queue.c.

References ao2_ref, ao2_t_find, ast_copy_string(), AST_DEVICE_INUSE, AST_DEVSTATE_CACHABLE, ast_devstate_changed(), dump_queue_members(), member::dynamic, member::interface, call_queue::members, call_queue::name, num_available_members(), OBJ_POINTER, member::realtime, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::rt_uniqueid.

Referenced by rqm_exec().

7541 {
7542  struct call_queue *q, tmpq = {
7543  .name = queuename,
7544  };
7545  struct member *mem, tmpmem;
7546  int res = RES_NOSUCHQUEUE;
7547 
7548  ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
7549  if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
7550  ao2_lock(q);
7551  if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
7552  /* XXX future changes should beware of this assumption!! */
7553  /*Change Penalty on realtime users*/
7554  if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
7555  update_realtime_member_field(mem, q->name, "penalty", "-1");
7556  } else if (!mem->dynamic) {
7557  ao2_ref(mem, -1);
7558  ao2_unlock(q);
7559  queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
7560  return RES_NOT_DYNAMIC;
7561  }
7562  queue_publish_member_blob(queue_member_removed_type(), queue_member_blob_create(q, mem));
7563 
7564  member_remove_from_queue(q, mem);
7565  ao2_ref(mem, -1);
7566 
7568  dump_queue_members(q);
7569  }
7570 
7571  if (!num_available_members(q)) {
7573  }
7574 
7575  res = RES_OKAY;
7576  } else {
7577  res = RES_EXISTS;
7578  }
7579  ao2_unlock(q);
7580  queue_t_unref(q, "Expiring temporary reference");
7581  }
7582 
7583  return res;
7584 }
int dynamic
Definition: app_queue.c:1735
#define RES_OKAY
Definition: app_queue.c:1565
#define OBJ_POINTER
Definition: astobj2.h:1150
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1611
#define RES_NOT_DYNAMIC
Definition: app_queue.c:1569
int realtime
Definition: app_queue.c:1736
struct ao2_container * members
Definition: app_queue.c:1887
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
static int num_available_members(struct call_queue *q)
Get the number of members available to accept a call.
Definition: app_queue.c:4509
const ast_string_field name
Definition: app_queue.c:1829
static void dump_queue_members(struct call_queue *pm_queue)
Dump all members in a specific queue to the database.
Definition: app_queue.c:7485
#define RES_NOSUCHQUEUE
Definition: app_queue.c:1568
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char rt_uniqueid[80]
Definition: app_queue.c:1750
#define ao2_t_find(container, arg, flags, tag)
Definition: astobj2.h:1734
#define RES_EXISTS
Definition: app_queue.c:1566
static int queue_persistent_members
queues.conf [general] option
Definition: app_queue.c:1590
static int request_withdraw_caller_from_queue ( const char *  queuename,
const char *  caller,
const char *  withdraw_info 
)
static

Request to withdraw a caller from a queue.

Return values
RES_NOSUCHQUEUEqueue does not exist
RES_OKAYwithdraw request sent
RES_NOT_CALLERqueue exists but no caller
RES_EXISTSa withdraw request was already sent for this caller (channel) and queue
Note
Ensure the appropriate realtime queue is loaded. Note that this short-circuits if the queue is already in memory.

Definition at line 7722 of file app_queue.c.

References ast_debug, ast_strdup, queue_ent::chan, find_load_queue_rt_friendly(), call_queue::head, queue_ent::next, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_CALLER, RES_OKAY, queue_ent::withdraw, and queue_ent::withdraw_info.

7723 {
7724  struct call_queue *q;
7725  struct queue_ent *qe;
7726  int res = RES_NOSUCHQUEUE;
7727 
7728  /*! \note Ensure the appropriate realtime queue is loaded. Note that this
7729  * short-circuits if the queue is already in memory. */
7730  if (!(q = find_load_queue_rt_friendly(queuename))) {
7731  return res;
7732  }
7733 
7734  ao2_lock(q);
7735  res = RES_NOT_CALLER;
7736  for (qe = q->head; qe; qe = qe->next) {
7737  if (!strcmp(ast_channel_name(qe->chan), caller)) {
7738  if (qe->withdraw) {
7739  ast_debug(1, "Ignoring duplicate withdraw request of caller %s from queue %s\n", caller, queuename);
7740  res = RES_EXISTS;
7741  } else {
7742  ast_debug(1, "Requested withdraw of caller %s from queue %s\n", caller, queuename);
7743  /* It is not possible to change the withdraw info by further withdraw requests for this caller (channel)
7744  in this queue, so we do not need to worry about a memory leak here. */
7745  if (withdraw_info) {
7747  }
7748  qe->withdraw = 1;
7749  res = RES_OKAY;
7750  }
7751  break;
7752  }
7753  }
7754  ao2_unlock(q);
7755  queue_unref(q);
7756 
7757  return res;
7758 }
#define RES_OKAY
Definition: app_queue.c:1565
struct ast_channel * chan
Definition: app_queue.c:1720
static struct call_queue * find_load_queue_rt_friendly(const char *queuename)
Definition: app_queue.c:3895
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
struct queue_ent * head
Definition: app_queue.c:1888
#define RES_NOT_CALLER
Definition: app_queue.c:1570
#define ast_debug(level,...)
Log a DEBUG message.
unsigned int withdraw
Definition: app_queue.c:1718
#define RES_NOSUCHQUEUE
Definition: app_queue.c:1568
char * withdraw_info
Definition: app_queue.c:1719
#define RES_EXISTS
Definition: app_queue.c:1566
struct queue_ent * next
Definition: app_queue.c:1723
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 4753 of file app_queue.c.

References ast_party_caller::ani, ast_party_connected_line::ani, ao2_bump, ast_call(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock_both, ast_channel_publish_dial(), ast_channel_req_accountcodes_precious(), AST_CHANNEL_REQUESTOR_BRIDGE_PEER, ast_channel_set_caller_event(), ast_connected_line_copy_from_caller(), ast_copy_string(), ast_json_pack(), ast_json_unref(), ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_pre_call(), ast_request(), ast_set_callerid(), ast_strdup, queue_ent::cancel_answered_elsewhere, queue_ent::chan, callattempt::dial_callerid_absent, do_hang(), ast_party_caller::id, ast_party_connected_line::id, callattempt::interface, queue_ent::linpos, member::membername, call_queue::name, ast_party_id::number, ast_party_dialed::number, callattempt::orig_chan_name, queue_ent::parent, pbx_builtin_setvar_helper(), queue_ent::predial_callee, RAII_VAR, call_queue::rrpos, member::status, callattempt::stillgoing, ast_party_dialed::str, ast_party_dialed::transit_network_select, and ast_party_number::valid.

Referenced by ring_one().

4754 {
4755  int res;
4756  int status;
4757  char tech[256];
4758  char *location;
4759  struct ast_format_cap *nativeformats;
4760  RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
4761 
4762  /* on entry here, we know that tmp->chan == NULL */
4763  if (!can_ring_entry(qe, tmp)) {
4764  tmp->stillgoing = 0;
4765  ++*busies;
4766  return 0;
4767  }
4768 
4769  ast_copy_string(tech, tmp->interface, sizeof(tech));
4770  if ((location = strchr(tech, '/'))) {
4771  *location++ = '\0';
4772  } else {
4773  location = "";
4774  }
4775 
4776  ast_channel_lock(qe->chan);
4777  nativeformats = ao2_bump(ast_channel_nativeformats(qe->chan));
4778  ast_channel_unlock(qe->chan);
4779 
4780  /* Request the peer */
4781  tmp->chan = ast_request(tech, nativeformats, NULL, qe->chan, location, &status);
4782  ao2_cleanup(nativeformats);
4783  if (!tmp->chan) { /* If we can't, just go on to the next call */
4784  ao2_lock(qe->parent);
4785  qe->parent->rrpos++;
4786  qe->linpos++;
4787  ao2_unlock(qe->parent);
4788 
4789  pending_members_remove(tmp->member);
4790 
4791  publish_dial_end_event(qe->chan, tmp, NULL, "BUSY");
4792  tmp->stillgoing = 0;
4793  ++*busies;
4794  return 0;
4795  }
4796 
4797  ast_channel_lock_both(tmp->chan, qe->chan);
4798 
4801  if (qe->cancel_answered_elsewhere) {
4802  ast_channel_hangupcause_set(tmp->chan, AST_CAUSE_ANSWERED_ELSEWHERE);
4803  }
4804  ast_channel_appl_set(tmp->chan, "AppQueue");
4805  ast_channel_data_set(tmp->chan, "(Outgoing Line)");
4806  memset(ast_channel_whentohangup(tmp->chan), 0, sizeof(*ast_channel_whentohangup(tmp->chan)));
4807 
4808  /* If the new channel has no callerid, try to guess what it should be */
4809  if (!ast_channel_caller(tmp->chan)->id.number.valid) {
4810  if (ast_channel_connected(qe->chan)->id.number.valid) {
4811  struct ast_party_caller caller;
4812 
4813  ast_party_caller_set_init(&caller, ast_channel_caller(tmp->chan));
4814  caller.id = ast_channel_connected(qe->chan)->id;
4815  caller.ani = ast_channel_connected(qe->chan)->ani;
4816  ast_channel_set_caller_event(tmp->chan, &caller, NULL);
4817  } else if (!ast_strlen_zero(ast_channel_dialed(qe->chan)->number.str)) {
4818  ast_set_callerid(tmp->chan, ast_channel_dialed(qe->chan)->number.str, NULL, NULL);
4819  } else if (!ast_strlen_zero(ast_channel_exten(qe->chan))) {
4820  ast_set_callerid(tmp->chan, ast_channel_exten(qe->chan), NULL, NULL);
4821  }
4822  tmp->dial_callerid_absent = 1;
4823  }
4824 
4825  ast_party_redirecting_copy(ast_channel_redirecting(tmp->chan), ast_channel_redirecting(qe->chan));
4826 
4827  ast_channel_dialed(tmp->chan)->transit_network_select = ast_channel_dialed(qe->chan)->transit_network_select;
4828 
4829  ast_connected_line_copy_from_caller(ast_channel_connected(tmp->chan), ast_channel_caller(qe->chan));
4830 
4831  /* Inherit specially named variables from parent channel */
4832  ast_channel_inherit_variables(qe->chan, tmp->chan);
4833  ast_channel_datastore_inherit(qe->chan, tmp->chan);
4834  ast_max_forwards_decrement(tmp->chan);
4835 
4836  /* Presense of ADSI CPE on outgoing channel follows ours */
4837  ast_channel_adsicpe_set(tmp->chan, ast_channel_adsicpe(qe->chan));
4838 
4839  /* Inherit context and extension */
4840  ast_channel_dialcontext_set(tmp->chan, ast_channel_context(qe->chan));
4841  ast_channel_exten_set(tmp->chan, ast_channel_exten(qe->chan));
4842 
4843  /* Save the original channel name to detect call pickup masquerading in. */
4844  tmp->orig_chan_name = ast_strdup(ast_channel_name(tmp->chan));
4845 
4846  ast_channel_unlock(tmp->chan);
4847  ast_channel_unlock(qe->chan);
4848 
4849  /* location is tmp->interface where tech/ has been stripped, so it follow the same syntax as DIALEDPEERNUMBER in app_dial.c */
4850  pbx_builtin_setvar_helper(tmp->chan, "DIALEDPEERNUMBER", strlen(location) ? location : tmp->interface);
4851 
4852  /* PREDIAL: Run gosub on the callee's channel */
4853  if (qe->predial_callee) {
4854  ast_pre_call(tmp->chan, qe->predial_callee);
4855  }
4856 
4857  /* Place the call, but don't wait on the answer */
4858  if ((res = ast_call(tmp->chan, location, 0))) {
4859  /* Again, keep going even if there's an error */
4860  ast_verb(3, "Couldn't call %s\n", tmp->interface);
4861  do_hang(tmp);
4862  ++*busies;
4863  return 0;
4864  }
4865 
4866  ast_channel_lock_both(tmp->chan, qe->chan);
4867 
4868  blob = ast_json_pack("{s: s, s: s, s: s}",
4869  "Queue", qe->parent->name,
4870  "Interface", tmp->interface,
4871  "MemberName", tmp->member->membername);
4872  queue_publish_multi_channel_blob(qe->chan, tmp->chan, queue_agent_called_type(), blob);
4873 
4874  ast_channel_publish_dial(qe->chan, tmp->chan, tmp->interface, NULL);
4875 
4876  ast_channel_unlock(tmp->chan);
4877  ast_channel_unlock(qe->chan);
4878 
4879  ast_verb(3, "Called %s\n", tmp->interface);
4880 
4881  return 1;
4882 }
void ast_set_callerid(struct ast_channel *chan, const char *cid_num, const char *cid_name, const char *cid_ani)
Set caller ID number, name and ANI and generate AMI event.
Definition: channel.c:7334
struct call_queue * parent
Definition: app_queue.c:1693
void ast_channel_req_accountcodes_precious(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Definition: channel.c:6439
char * str
Subscriber phone number (Malloced)
Definition: channel.h:386
void ast_channel_set_caller_event(struct ast_channel *chan, const struct ast_party_caller *caller, const struct ast_set_party_caller *update)
Set the caller id information in the Asterisk channel and generate an AMI event if the caller id name...
Definition: channel.c:7372
struct ast_channel * chan
Definition: app_queue.c:1720
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
struct ast_party_id id
Connected party ID.
Definition: channel.h:458
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer, const char *dialstring, const char *dialstatus)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition: channel.c:6461
char interface[256]
Definition: app_queue.c:1673
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
char membername[80]
Definition: app_queue.c:1732
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6354
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition: astobj2.h:480
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
Inherit datastores from a parent to a child.
Definition: channel.c:2368
const char * predial_callee
Definition: app_queue.c:1698
Caller Party information.
Definition: channel.h:418
struct ast_party_id ani
Automatic Number Identification (ANI)
Definition: channel.h:465
int linpos
Definition: app_queue.c:1713
int ast_pre_call(struct ast_channel *chan, const char *sub_args)
Execute a Gosub call on the channel before a call is placed.
Definition: channel.c:6444
void ast_party_caller_set_init(struct ast_party_caller *init, const struct ast_party_caller *guide)
Initialize the given caller structure using the given guide for a set update operation.
Definition: channel.c:1999
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
char * orig_chan_name
Definition: app_queue.c:1688
const ast_string_field name
Definition: app_queue.c:1829
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
Inherits channel variable from parent to child channel.
Definition: channel.c:6771
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2929
int transit_network_select
Transit Network Select.
Definition: channel.h:397
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
Definition: channel.c:8293
struct ast_party_dialed::@206 number
Dialed/Called number.
Abstract JSON element (object, array, string, int, ...).
static void do_hang(struct callattempt *o)
common hangup actions
Definition: app_queue.c:4627
int cancel_answered_elsewhere
Definition: app_queue.c:1717
unsigned int dial_callerid_absent
Definition: app_queue.c:1683
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
unsigned int stillgoing
Definition: app_queue.c:1685
void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src)
Copy the source redirecting information to the destination redirecting.
Definition: channel.c:2135
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
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 4910 of file app_queue.c.

References ast_autoservice_start(), ast_autoservice_stop(), ast_debug, queue_ent::chan, queue_ent::expire, find_best(), callattempt::interface, queue_ent::parent, queue_ent::predial_callee, ring_entry(), and callattempt::stillgoing.

Referenced by wait_for_answer().

4911 {
4912  int ret = 0;
4913  struct callattempt *cur;
4914 
4915  if (qe->predial_callee) {
4917  for (cur = outgoing; cur; cur = cur->q_next) {
4918  if (cur->stillgoing && cur->chan) {
4919  ast_autoservice_start(cur->chan);
4920  }
4921  }
4922  }
4923 
4924  while (ret == 0) {
4925  struct callattempt *best = find_best(outgoing);
4926  if (!best) {
4927  ast_debug(1, "Nobody left to try ringing in queue\n");
4928  break;
4929  }
4930  if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
4931  /* Ring everyone who shares this best metric (for ringall) */
4932  for (cur = outgoing; cur; cur = cur->q_next) {
4933  if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
4934  ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
4935  ret |= ring_entry(qe, cur, busies);
4936  if (qe->predial_callee && cur->chan) {
4937  ast_autoservice_start(cur->chan);
4938  }
4939  }
4940  }
4941  } else {
4942  /* Ring just the best channel */
4943  ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
4944  ret = ring_entry(qe, best, busies);
4945  if (qe->predial_callee && best->chan) {
4946  ast_autoservice_start(best->chan);
4947  }
4948  }
4949 
4950  /* If we have timed out, break out */
4951  if (qe->expire && (time(NULL) >= qe->expire)) {
4952  ast_debug(1, "Queue timed out while ringing members.\n");
4953  ret = 0;
4954  break;
4955  }
4956  }
4957  if (qe->predial_callee) {
4958  for (cur = outgoing; cur; cur = cur->q_next) {
4959  if (cur->stillgoing && cur->chan) {
4960  ast_autoservice_stop(cur->chan);
4961  }
4962  }
4964  }
4965 
4966  return ret;
4967 }
struct call_queue * parent
Definition: app_queue.c:1693
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
struct ast_channel * chan
Definition: app_queue.c:1720
time_t expire
Definition: app_queue.c:1716
char interface[256]
Definition: app_queue.c:1673
const char * predial_callee
Definition: app_queue.c:1698
static struct callattempt * find_best(struct callattempt *outgoing)
find the entry with the best metric, or NULL
Definition: app_queue.c:4885
#define ast_debug(level,...)
Log a DEBUG message.
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
static int ring_entry(struct queue_ent *qe, struct callattempt *tmp, int *busies)
Part 2 of ring_one.
Definition: app_queue.c:4753
We define a custom "local user" structure because we use it not only for keeping track of what is in ...
Definition: app_queue.c:1669
unsigned int stillgoing
Definition: app_queue.c:1685
static void rt_handle_member_record ( struct call_queue q,
char *  category,
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 3578 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ast_copy_string(), AST_DEVSTATE_CACHABLE, ast_devstate_changed(), ast_false(), ast_true(), create_queue_member(), member::dead, member::interface, member::lastpause, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::reason_paused, member::ringinuse, member::rt_uniqueid, S_OR, member::state_interface, and member::wrapuptime.

Referenced by find_queue_by_name_rt().

3579 {
3580  struct member *m;
3581  struct ao2_iterator mem_iter;
3582  int penalty = 0;
3583  int paused = 0;
3584  int found = 0;
3585  int wrapuptime = 0;
3586  int ringinuse = q->ringinuse;
3587 
3588  const char *config_val;
3589  const char *interface = ast_variable_retrieve(member_config, category, "interface");
3590  const char *rt_uniqueid = ast_variable_retrieve(member_config, category, "uniqueid");
3591  const char *membername = S_OR(ast_variable_retrieve(member_config, category, "membername"), interface);
3592  const char *state_interface = S_OR(ast_variable_retrieve(member_config, category, "state_interface"), interface);
3593  const char *penalty_str = ast_variable_retrieve(member_config, category, "penalty");
3594  const char *paused_str = ast_variable_retrieve(member_config, category, "paused");
3595  const char *wrapuptime_str = ast_variable_retrieve(member_config, category, "wrapuptime");
3596  const char *reason_paused = ast_variable_retrieve(member_config, category, "reason_paused");
3597 
3598  if (ast_strlen_zero(rt_uniqueid)) {
3599  ast_log(LOG_WARNING, "Realtime field 'uniqueid' is empty for member %s\n",
3600  S_OR(membername, "NULL"));
3601  return;
3602  }
3603 
3604  if (ast_strlen_zero(interface)) {
3605  ast_log(LOG_WARNING, "Realtime field 'interface' is empty for member %s\n",
3606  S_OR(membername, "NULL"));
3607  return;
3608  }
3609 
3610  if (penalty_str) {
3611  penalty = atoi(penalty_str);
3612  if ((penalty < 0) && negative_penalty_invalid) {
3613  return;
3614  } else if (penalty < 0) {
3615  penalty = 0;
3616  }
3617  }
3618 
3619  if (paused_str) {
3620  paused = atoi(paused_str);
3621  if (paused < 0) {
3622  paused = 0;
3623  }
3624  }
3625 
3626  if (wrapuptime_str) {
3627  wrapuptime = atoi(wrapuptime_str);
3628  if (wrapuptime < 0) {
3629  wrapuptime = 0;
3630  }
3631  }
3632 
3633  if ((config_val = ast_variable_retrieve(member_config, category, realtime_ringinuse_field))) {
3634  if (ast_true(config_val)) {
3635  ringinuse = 1;
3636  } else if (ast_false(config_val)) {
3637  ringinuse = 0;
3638  } else {
3639  ast_log(LOG_WARNING, "Invalid value of '%s' field for %s in queue '%s'\n", realtime_ringinuse_field, interface, q->name);
3640  }
3641  }
3642 
3643  /* Find member by realtime uniqueid and update */
3644  mem_iter = ao2_iterator_init(q->members, 0);
3645  while ((m = ao2_iterator_next(&mem_iter))) {
3646  if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
3647  m->dead = 0; /* Do not delete this one. */
3648  ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3649  if (paused_str) {
3650  m->paused = paused;
3651  if (paused && m->lastpause == 0) {
3652  time(&m->lastpause); /* XXX: Should this come from realtime? */
3653  }
3654  ast_devstate_changed(m->paused ? QUEUE_PAUSED_DEVSTATE : QUEUE_UNPAUSED_DEVSTATE,
3655  AST_DEVSTATE_CACHABLE, "Queue:%s_pause_%s", q->name, m->interface);
3656  }
3657  if (strcasecmp(state_interface, m->state_interface)) {
3658  ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
3659  }
3660  m->penalty = penalty;
3661  m->ringinuse = ringinuse;
3662  m->wrapuptime = wrapuptime;
3663  if (realtime_reason_paused) {
3664  ast_copy_string(m->reason_paused, S_OR(reason_paused, ""), sizeof(m->reason_paused));
3665  }
3666  found = 1;
3667  ao2_ref(m, -1);
3668  break;
3669  }
3670  ao2_ref(m, -1);
3671  }
3672  ao2_iterator_destroy(&mem_iter);
3673 
3674  /* Create a new member */
3675  if (!found) {
3676  if ((m = create_queue_member(interface, membername, penalty, paused, state_interface, ringinuse, wrapuptime))) {
3677  m->dead = 0;
3678  m->realtime = 1;
3679  ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
3680  if (!ast_strlen_zero(reason_paused)) {
3681  ast_copy_string(m->reason_paused, reason_paused, sizeof(m->reason_paused));
3682  }
3683  if (!log_membername_as_agent) {
3684  ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3685  } else {
3686  ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
3687  }
3688  member_add_to_queue(q, m);
3689  ao2_ref(m, -1);
3690  m = NULL;
3691  }
3692  }
3693 }
static struct member * create_queue_member(const char *interface, const char *membername, int penalty, int paused, const char *state_interface, int ringinuse, int wrapuptime)
allocate space for new queue member and set fields based on parameters passed
Definition: app_queue.c:2865
int paused
Definition: app_queue.c:1738
char state_interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1730
int wrapuptime
Definition: app_queue.c:1742
static int negative_penalty_invalid
queues.conf [general] option
Definition: app_queue.c:1611
static int log_membername_as_agent
queues.conf [general] option
Definition: app_queue.c:1614
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int realtime
Definition: app_queue.c:1736
unsigned int dead
Definition: app_queue.c:1748
struct ao2_container * members
Definition: app_queue.c:1887
char membername[80]
Definition: app_queue.c:1732
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
static char * realtime_ringinuse_field
name of the ringinuse field in the realtime database
Definition: app_queue.c:1620
int ast_devstate_changed(enum ast_device_state state, enum ast_devstate_cache cachable, const char *fmt,...)
Tells Asterisk the State for Device is changed.
Definition: devicestate.c:510
time_t lastpause
Definition: app_queue.c:1745
int penalty
Definition: app_queue.c:1733
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: utils.c:2199
const ast_string_field name
Definition: app_queue.c:1829
char reason_paused[80]
Definition: app_queue.c:1739
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
char rt_uniqueid[80]
Definition: app_queue.c:1750
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
static int realtime_reason_paused
does realtime backend support reason_paused
Definition: app_queue.c:1623
unsigned int ringinuse
Definition: app_queue.c:1751
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
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 5794 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

5795 {
5796  int max_penalty = INT_MAX;
5797 
5798  if (qe->max_penalty != INT_MAX) {
5799  char max_penalty_str[20];
5800 
5801  if (qe->pr->max_relative) {
5802  max_penalty = qe->max_penalty + qe->pr->max_value;
5803  } else {
5804  max_penalty = qe->pr->max_value;
5805  }
5806 
5807  /* a relative change to the penalty could put it below 0 */
5808  if (max_penalty < 0) {
5809  max_penalty = 0;
5810  }
5811 
5812  snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
5813  pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
5814  qe->max_penalty = max_penalty;
5815  ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
5816  qe->max_penalty, ast_channel_name(qe->chan), qe->pr->time);
5817  }
5818 
5819  if (qe->min_penalty != INT_MAX) {
5820  char min_penalty_str[20];
5821  int min_penalty;
5822 
5823  if (qe->pr->min_relative) {
5824  min_penalty = qe->min_penalty + qe->pr->min_value;
5825  } else {
5826  min_penalty = qe->pr->min_value;
5827  }
5828 
5829  /* a relative change to the penalty could put it below 0 */
5830  if (min_penalty < 0) {
5831  min_penalty = 0;
5832  }
5833 
5834  if (max_penalty != INT_MAX && min_penalty > max_penalty) {
5835  min_penalty = max_penalty;
5836  }
5837 
5838  snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
5839  pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
5840  qe->min_penalty = min_penalty;
5841  ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
5842  qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
5843  }
5844 
5845  if (qe->raise_penalty != INT_MAX) {
5846  char raise_penalty_str[20];
5847  int raise_penalty;
5848 
5849  if (qe->pr->raise_relative) {
5850  raise_penalty = qe->raise_penalty + qe->pr->raise_value;
5851  } else {
5852  raise_penalty = qe->pr->raise_value;
5853  }
5854 
5855  /* a relative change to the penalty could put it below 0 */
5856  if (raise_penalty < 0) {
5857  raise_penalty = 0;
5858  }
5859 
5860  if (max_penalty != INT_MAX && raise_penalty > max_penalty) {
5861  raise_penalty = max_penalty;
5862  }
5863 
5864  snprintf(raise_penalty_str, sizeof(raise_penalty_str), "%d", raise_penalty);
5865  pbx_builtin_setvar_helper(qe->chan, "QUEUE_RAISE_PENALTY", raise_penalty_str);
5866  qe->raise_penalty = raise_penalty;
5867  ast_debug(3, "Setting raised penalty to %d for caller %s since %d seconds have elapsed\n",
5868  qe->raise_penalty, ast_channel_name(qe->chan), qe->pr->time);
5869  }
5870 
5871  qe->pr = AST_LIST_NEXT(qe->pr, list);
5872 }
int raise_penalty
Definition: app_queue.c:1712
struct ast_channel * chan
Definition: app_queue.c:1720
int min_penalty
Definition: app_queue.c:1711
int max_relative
Definition: app_queue.c:1780
int max_penalty
Definition: app_queue.c:1710
int min_relative
Definition: app_queue.c:1781
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
#define ast_debug(level,...)
Log a DEBUG message.
int raise_relative
Definition: app_queue.c:1782
struct penalty_rule * pr
Definition: app_queue.c:1722
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
static int update_queue ( struct call_queue q,
struct member member,
int  callcompletedinsl,
time_t  starttime 
)
static

update the queue status

Return values
0always

Definition at line 5972 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, member::callcompletedinsl, member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, call_queue::members, OBJ_POINTER, member::starttime, and call_queue::talktime.

Referenced by handle_attended_transfer(), handle_blind_transfer(), and update_status().

5973 {
5974  int oldtalktime;
5975  int newtalktime = time(NULL) - starttime;
5976  struct member *mem;
5977  struct call_queue *qtmp;
5978  struct ao2_iterator queue_iter;
5979 
5980  /* It is possible for us to be called when a call has already been considered terminated
5981  * and data updated, so to ensure we only act on the call that the agent is currently in
5982  * we check when the call was bridged.
5983  */
5984  if (!starttime || (member->starttime != starttime)) {
5985  return 0;
5986  }
5987 
5988  if (shared_lastcall) {
5989  queue_iter = ao2_iterator_init(queues, 0);
5990  while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
5991  ao2_lock(qtmp);
5992  if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
5993  time(&mem->lastcall);
5994  mem->calls++;
5995  mem->callcompletedinsl = 0;
5996  mem->starttime = 0;
5997  mem->lastqueue = q;
5998  ao2_ref(mem, -1);
5999  }
6000  ao2_unlock(qtmp);
6001  queue_t_unref(qtmp, "Done with iterator");
6002  }
6003  ao2_iterator_destroy(&queue_iter);
6004  } else {
6005  ao2_lock(q);
6006  time(&member->lastcall);
6007  member->callcompletedinsl = 0;
6008  member->calls++;
6009  member->starttime = 0;
6010  member->lastqueue = q;
6011  ao2_unlock(q);
6012  }
6013  /* Member might never experience any direct status change (local
6014  * channel with forwarding in particular). If that's the case,
6015  * this is the last chance to remove it from pending or subsequent
6016  * calls will not occur.
6017  */
6018  pending_members_remove(member);
6019 
6020  ao2_lock(q);
6021  q->callscompleted++;
6022  if (callcompletedinsl) {
6023  q->callscompletedinsl++;
6024  }
6025  if (q->callscompleted == 1) {
6026  q->talktime = newtalktime;
6027  } else {
6028  /* Calculate talktime using the same exponential average as holdtime code */
6029  oldtalktime = q->talktime;
6030  q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
6031  }
6032  ao2_unlock(q);
6033  return 0;
6034 }
#define OBJ_POINTER
Definition: astobj2.h:1150
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
struct ao2_container * members
Definition: app_queue.c:1887
int callscompletedinsl
Definition: app_queue.c:1866
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
struct call_queue * lastqueue
Definition: app_queue.c:1747
int talktime
Definition: app_queue.c:1861
time_t starttime
Definition: app_queue.c:1743
int calls
Definition: app_queue.c:1734
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
int callcompletedinsl
Definition: app_queue.c:1741
int callscompleted
Definition: app_queue.c:1862
static int shared_lastcall
queues.conf [general] option
Definition: app_queue.c:1602
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
time_t lastcall
Definition: app_queue.c:1744
static void 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 2575 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, member::callcompletedinsl, member::starttime, member::status, and update_queue().

Referenced by device_state_cb().

2576 {
2577  if (m->status != status) {
2578  /* If this member has transitioned to being available then update their queue
2579  * information. If they are currently in a call then the leg to the agent will be
2580  * considered done and the call finished.
2581  */
2582  if (status == AST_DEVICE_NOT_INUSE) {
2584  }
2585 
2586  m->status = status;
2587 
2588  /* Remove the member from the pending members pool only when the status changes.
2589  * This is not done unconditionally because we can occasionally see multiple
2590  * device state notifications of not in use after a previous call has ended,
2591  * including after we have initiated a new call. This is more likely to
2592  * happen when there is latency in the connection to the member.
2593  */
2594  pending_members_remove(m);
2595 
2596  queue_publish_member_blob(queue_member_status_type(), queue_member_blob_create(q, m));
2597  }
2598 }
static int update_queue(struct call_queue *q, struct member *member, int callcompletedinsl, time_t starttime)
update the queue status
Definition: app_queue.c:5972
time_t starttime
Definition: app_queue.c:1743
int status
Definition: app_queue.c:1737
int callcompletedinsl
Definition: app_queue.c:1741
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 4172 of file app_queue.c.

References ast_canmatch_extension(), ast_goto_if_exists(), 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(), wait_for_answer(), and wait_our_turn().

4173 {
4174  int digitlen = strlen(qe->digits);
4175 
4176  /* Prevent possible buffer overflow */
4177  if (digitlen < sizeof(qe->digits) - 2) {
4178  qe->digits[digitlen] = digit;
4179  qe->digits[digitlen + 1] = '\0';
4180  } else {
4181  qe->digits[0] = '\0';
4182  return 0;
4183  }
4184 
4185  /* If there's no context to goto, short-circuit */
4186  if (ast_strlen_zero(qe->context)) {
4187  return 0;
4188  }
4189 
4190  /* If the extension is bad, then reset the digits to blank */
4191  if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
4192  S_COR(ast_channel_caller(qe->chan)->id.number.valid, ast_channel_caller(qe->chan)->id.number.str, NULL))) {
4193  qe->digits[0] = '\0';
4194  return 0;
4195  }
4196 
4197  /* We have an exact match */
4198  if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
4199  qe->valid_digits = 1;
4200  /* Return 1 on a successful goto */
4201  return 1;
4202  }
4203 
4204  return 0;
4205 }
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
char digits[AST_MAX_EXTENSION]
Definition: app_queue.c:1697
struct ast_channel * chan
Definition: app_queue.c:1720
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition: pbx.c:4190
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
int valid_digits
Definition: app_queue.c:1699
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8781
char context[AST_MAX_CONTEXT]
Definition: app_queue.c:1696
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
static struct callattempt* wait_for_answer ( struct queue_ent qe,
struct callattempt outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect,
int  forwardsallowed 
)
static

Wait for a member to answer the call.

Parameters
[in]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 integrated into and replaced by ast_call_forward()

Definition at line 5205 of file app_queue.c.

References ast_aoc_decode(), ast_aoc_destroy_decoded(), ast_aoc_destroy_encoded(), ast_aoc_encode(), ast_aoc_get_msg_type(), ast_call(), ast_channel_connected_line_sub(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock_both, AST_CHANNEL_NAME, ast_channel_publish_dial(), ast_channel_publish_dial_forward(), ast_channel_redirecting_sub(), ast_channel_req_accountcodes(), AST_CHANNEL_REQUESTOR_BRIDGE_PEER, ast_channel_update_connected_line(), ast_channel_update_redirecting(), ast_connected_line_copy_from_caller(), ast_connected_line_parse_data(), 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_hangup(), ast_indicate(), ast_indicate_data(), AST_MAX_WATCHERS, ast_moh_stop(), ast_party_connected_line_copy(), ast_party_connected_line_free(), 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_read(), ast_remaining_ms(), ast_request(), AST_STATE_UP, ast_strdup, ast_strdupa, ast_tvdiff_ms(), ast_tvnow(), ast_waitfor_n(), callattempt::block_connected_update, queue_ent::chan, callattempt::connected, ast_frame::data, ast_frame::datalen, callattempt::dial_callerid_absent, do_hang(), ast_frame::frametype, ast_party_redirecting::from, ast_party_caller::id, ast_frame_subclass::integer, callattempt::interface, member::interface, member::membername, call_queue::name, ast_party_id::number, callattempt::orig_chan_name, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), callattempt::pending_connected_update, ring_one(), queue_ent::ring_when_ringing, rna(), callattempt::stillgoing, ast_party_number::str, ast_frame::subclass, ast_channel::tech, ast_party_dialed::transit_network_select, ast_party_number::valid, and valid_exit().

Referenced by dial_exec_full().

5206 {
5207  const char *queue = qe->parent->name;
5208  struct callattempt *o, *start = NULL, *prev = NULL;
5209  int status;
5210  int numbusies = prebusies;
5211  int numnochan = 0;
5212  int stillgoing = 0;
5213  int orig = *to;
5214  struct ast_frame *f;
5215  struct callattempt *peer = NULL;
5216  struct ast_channel *winner;
5217  struct ast_channel *in = qe->chan;
5218  char on[80] = "";
5219  char membername[80] = "";
5220  long starttime = 0;
5221  long endtime = 0;
5222  char *inchan_name;
5223  struct timeval start_time_tv = ast_tvnow();
5224  int canceled_by_caller = 0; /* 1 when caller hangs up or press digit or press * */
5225 
5226  ast_channel_lock(qe->chan);
5227  inchan_name = ast_strdupa(ast_channel_name(qe->chan));
5228  ast_channel_unlock(qe->chan);
5229 
5230  starttime = (long) time(NULL);
5231 
5232  while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
5233  int numlines, retry, pos = 1;
5234  struct ast_channel *watchers[AST_MAX_WATCHERS];
5235  watchers[0] = in;
5236  start = NULL;
5237 
5238  for (retry = 0; retry < 2; retry++) {
5239  numlines = 0;
5240  for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
5241  if (o->stillgoing) { /* Keep track of important channels */
5242  stillgoing = 1;
5243  if (o->chan) {
5244  if (pos < AST_MAX_WATCHERS) {
5245  watchers[pos++] = o->chan;
5246  }
5247  if (!start) {
5248  start = o;
5249  } else {
5250  prev->call_next = o;
5251  }
5252  prev = o;
5253  }
5254  } else if (prev) {
5255  prev->call_next = NULL;
5256  }
5257  numlines++;
5258  }
5259  if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
5260  (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) {
5261  break;
5262  }
5263  /* On "ringall" strategy we only move to the next penalty level
5264  when *all* ringing phones are done in the current penalty level */
5265  ring_one(qe, outgoing, &numbusies);
5266  /* and retry... */
5267  }
5268  if (pos == 1 /* not found */) {
5269  if (numlines == (numbusies + numnochan)) {
5270  ast_debug(1, "Everyone is busy at this time\n");
5271  } else {
5272  ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
5273  }
5274  *to = 0;
5275  return NULL;
5276  }
5277 
5278  /* Poll for events from both the incoming channel as well as any outgoing channels */
5279  winner = ast_waitfor_n(watchers, pos, to);
5280 
5281  /* Service all of the outgoing channels */
5282  for (o = start; o; o = o->call_next) {
5283  /* We go with a fixed buffer here instead of using ast_strdupa. Using
5284  * ast_strdupa in a loop like this one can cause a stack overflow
5285  */
5286  char ochan_name[AST_CHANNEL_NAME];
5287 
5288  if (o->chan) {
5289  ast_channel_lock(o->chan);
5290  ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
5291  ast_channel_unlock(o->chan);
5292  }
5293  if (o->stillgoing && (o->chan) && (ast_channel_state(o->chan) == AST_STATE_UP)) {
5294  if (!peer) {
5295  ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5296  if (o->orig_chan_name
5297  && strcmp(o->orig_chan_name, ochan_name)) {
5298  /*
5299  * The channel name changed so we must generate COLP update.
5300  * Likely because a call pickup channel masqueraded in.
5301  */
5302  update_connected_line_from_peer(in, o->chan, 1);
5303  } else if (!o->block_connected_update) {
5304  if (o->pending_connected_update) {
5305  if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0)) {
5307  }
5308  } else if (!o->dial_callerid_absent) {
5309  update_connected_line_from_peer(in, o->chan, 1);
5310  }
5311  }
5312  if (o->aoc_s_rate_list) {
5313  size_t encoded_size;
5314  struct ast_aoc_encoded *encoded;
5315  if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5316  ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5317  ast_aoc_destroy_encoded(encoded);
5318  }
5319  }
5320  peer = o;
5321  }
5322  } else if (o->chan && (o->chan == winner)) {
5323 
5324  ast_copy_string(on, o->member->interface, sizeof(on));
5325  ast_copy_string(membername, o->member->membername, sizeof(membername));
5326 
5327  /* Before processing channel, go ahead and check for forwarding */
5328  if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
5329  ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
5330  ast_channel_publish_dial_forward(qe->chan, o->chan, NULL, NULL,
5331  "CANCEL", ast_channel_call_forward(o->chan));
5332  numnochan++;
5333  do_hang(o);
5334  winner = NULL;
5335  continue;
5336  } else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
5337  struct ast_channel *original = o->chan;
5338  char forwarder[AST_CHANNEL_NAME];
5339  char tmpchan[256];
5340  char *stuff;
5341  char *tech;
5342  int failed = 0;
5343 
5344  ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
5345  ast_copy_string(forwarder, ast_channel_name(o->chan), sizeof(forwarder));
5346  if ((stuff = strchr(tmpchan, '/'))) {
5347  *stuff++ = '\0';
5348  tech = tmpchan;
5349  } else {
5350  const char *forward_context;
5351  ast_channel_lock(o->chan);
5352  forward_context = pbx_builtin_getvar_helper(o->chan, "FORWARD_CONTEXT");
5353  snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), forward_context ? forward_context : ast_channel_context(o->chan));
5354  ast_channel_unlock(o->chan);
5355  stuff = tmpchan;
5356  tech = "Local";
5357  }
5358  if (!strcasecmp(tech, "Local")) {
5359  /*
5360  * Drop the connected line update block for local channels since
5361  * this is going to run dialplan and the user can change his
5362  * mind about what connected line information he wants to send.
5363  */
5364  o->block_connected_update = 0;
5365  }
5366 
5367  ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
5368  /* Setup parameters */
5369  o->chan = ast_request(tech, ast_channel_nativeformats(in), NULL, in, stuff, &status);
5370  if (!o->chan) {
5371  ast_log(LOG_NOTICE,
5372  "Forwarding failed to create channel to dial '%s/%s'\n",
5373  tech, stuff);
5374  o->stillgoing = 0;
5375  numnochan++;
5376  } else {
5377  ast_channel_lock_both(o->chan, original);
5378  ast_party_redirecting_copy(ast_channel_redirecting(o->chan),
5379  ast_channel_redirecting(original));
5380  ast_channel_unlock(o->chan);
5381  ast_channel_unlock(original);
5382 
5383  ast_channel_lock_both(o->chan, in);
5384  ast_channel_inherit_variables(in, o->chan);
5385  ast_channel_datastore_inherit(in, o->chan);
5386  pbx_builtin_setvar_helper(o->chan, "FORWARDERNAME", forwarder);
5387  ast_max_forwards_decrement(o->chan);
5388 
5389  if (o->pending_connected_update) {
5390  /*
5391  * Re-seed the callattempt's connected line information with
5392  * previously acquired connected line info from the queued
5393  * channel. The previously acquired connected line info could
5394  * have been set through the CONNECTED_LINE dialplan function.
5395  */
5396  o->pending_connected_update = 0;
5397  ast_party_connected_line_copy(&o->connected, ast_channel_connected(in));
5398  }
5399 
5400  ast_free(o->orig_chan_name);
5401  o->orig_chan_name = ast_strdup(ast_channel_name(o->chan));
5402 
5404 
5405  if (!ast_channel_redirecting(o->chan)->from.number.valid
5406  || ast_strlen_zero(ast_channel_redirecting(o->chan)->from.number.str)) {
5407  /*
5408  * The call was not previously redirected so it is
5409  * now redirected from this number.
5410  */
5411  ast_party_number_free(&ast_channel_redirecting(o->chan)->from.number);
5412  ast_party_number_init(&ast_channel_redirecting(o->chan)->from.number);
5413  ast_channel_redirecting(o->chan)->from.number.valid = 1;
5414  ast_channel_redirecting(o->chan)->from.number.str =
5415  ast_strdup(ast_channel_exten(in));
5416  }
5417 
5418  ast_channel_dialed(o->chan)->transit_network_select = ast_channel_dialed(in)->transit_network_select;
5419 
5420  o->dial_callerid_absent = !ast_channel_caller(o->chan)->id.number.valid
5421  || ast_strlen_zero(ast_channel_caller(o->chan)->id.number.str);
5422  ast_connected_line_copy_from_caller(ast_channel_connected(o->chan),
5423  ast_channel_caller(in));
5424 
5425  ast_channel_unlock(in);
5426  if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
5427  && !o->block_connected_update) {
5428  struct ast_party_redirecting redirecting;
5429 
5430  /*
5431  * Redirecting updates to the caller make sense only on single
5432  * call at a time strategies.
5433  *
5434  * Need to re-evaluate if calling unlock is still required as we no longer
5435  * use macro.
5436  */
5437  ast_party_redirecting_init(&redirecting);
5438  ast_party_redirecting_copy(&redirecting, ast_channel_redirecting(o->chan));
5439  ast_channel_unlock(o->chan);
5440  if (ast_channel_redirecting_sub(o->chan, in, &redirecting, 0)) {
5441  ast_channel_update_redirecting(in, &redirecting, NULL);
5442  }
5443  ast_party_redirecting_free(&redirecting);
5444  } else {
5445  ast_channel_unlock(o->chan);
5446  }
5447 
5448  if (ast_call(o->chan, stuff, 0)) {
5449  ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
5450  tech, stuff);
5451  failed = 1;
5452  }
5453  }
5454 
5455  ast_channel_publish_dial_forward(qe->chan, original, o->chan, NULL,
5456  "CANCEL", ast_channel_call_forward(original));
5457  if (o->chan) {
5458  ast_channel_publish_dial(qe->chan, o->chan, stuff, NULL);
5459  }
5460 
5461  if (failed) {
5462  do_hang(o);
5463  numnochan++;
5464  }
5465 
5466  /* Hangup the original channel now, in case we needed it */
5467  ast_hangup(winner);
5468  continue;
5469  }
5470  f = ast_read(winner);
5471  if (f) {
5472  if (f->frametype == AST_FRAME_CONTROL) {
5473  switch (f->subclass.integer) {
5474  case AST_CONTROL_ANSWER:
5475  /* This is our guy if someone answered. */
5476  if (!peer) {
5477  ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
5478  ast_channel_publish_dial(qe->chan, o->chan, on, "ANSWER");
5479  publish_dial_end_event(qe->chan, outgoing, o->chan, "CANCEL");
5480  if (o->orig_chan_name
5481  && strcmp(o->orig_chan_name, ochan_name)) {
5482  /*
5483  * The channel name changed so we must generate COLP update.
5484  * Likely because a call pickup channel masqueraded in.
5485  */
5486  update_connected_line_from_peer(in, o->chan, 1);
5487  } else if (!o->block_connected_update) {
5488  if (o->pending_connected_update) {
5489  if (ast_channel_connected_line_sub(o->chan, in, &o->connected, 0)) {
5491  }
5492  } else if (!o->dial_callerid_absent) {
5493  update_connected_line_from_peer(in, o->chan, 1);
5494  }
5495  }
5496  if (o->aoc_s_rate_list) {
5497  size_t encoded_size;
5498  struct ast_aoc_encoded *encoded;
5499  if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
5500  ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
5501  ast_aoc_destroy_encoded(encoded);
5502  }
5503  }
5504  peer = o;
5505  }
5506  break;
5507  case AST_CONTROL_BUSY:
5508  ast_verb(3, "%s is busy\n", ochan_name);
5509  ast_channel_publish_dial(qe->chan, o->chan, on, "BUSY");
5510  endtime = (long) time(NULL);
5511  endtime -= starttime;
5512  rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopausebusy);
5513  do_hang(o);
5514  if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
5515  if (qe->parent->timeoutrestart) {
5516  start_time_tv = ast_tvnow();
5517  }
5518  /* Have enough time for a queue member to answer? */
5519  if (ast_remaining_ms(start_time_tv, orig) > 500) {
5520  ring_one(qe, outgoing, &numbusies);
5521  starttime = (long) time(NULL);
5522  }
5523  }
5524  numbusies++;
5525  break;
5527  ast_verb(3, "%s is circuit-busy\n", ochan_name);
5528  ast_channel_publish_dial(qe->chan, o->chan, on, "CONGESTION");
5529  endtime = (long) time(NULL);
5530  endtime -= starttime;
5531  rna(endtime * 1000, qe, o->chan, on, membername, qe->parent->autopauseunavail);
5532  do_hang(o);
5533  if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
5534  if (qe->parent->timeoutrestart) {
5535  start_time_tv = ast_tvnow();
5536  }
5537  if (ast_remaining_ms(start_time_tv, orig) > 500) {
5538  ring_one(qe, outgoing, &numbusies);
5539  starttime = (long) time(NULL);
5540  }
5541  }
5542  numbusies++;
5543  break;
5544  case AST_CONTROL_RINGING:
5545  ast_verb(3, "%s is ringing\n", ochan_name);
5546 
5547  ast_channel_publish_dial(qe->chan, o->chan, on, "RINGING");
5548 
5549  /* Start ring indication when the channel is ringing, if specified */
5550  if (qe->ring_when_ringing) {
5551  ast_moh_stop(qe->chan);
5553  }
5554  break;
5555  case AST_CONTROL_OFFHOOK:
5556  /* Ignore going off hook */
5557  break;
5559  if (o->block_connected_update) {
5560  ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
5561  break;
5562  }
5563  if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
5564  struct ast_party_connected_line connected;
5565 
5566  ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
5568  ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
5569  ast_party_connected_line_set(&o->connected, &connected, NULL);
5570  ast_party_connected_line_free(&connected);
5571  o->pending_connected_update = 1;
5572  break;
5573  }
5574 
5575  /*
5576  * Prevent using the CallerID from the outgoing channel since we
5577  * got a connected line update from it.
5578  */
5579  o->dial_callerid_absent = 1;
5580 
5581  if (ast_channel_connected_line_sub(o->chan, in, f, 1)) {
5583  }
5584  break;
5585  case AST_CONTROL_AOC:
5586  {
5587  struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
5588  if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
5589  ast_aoc_destroy_decoded(o->aoc_s_rate_list);
5590  o->aoc_s_rate_list = decoded;
5591  } else {
5592  ast_aoc_destroy_decoded(decoded);
5593  }
5594  }
5595  break;
5597  if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
5598  /*
5599  * Redirecting updates to the caller make sense only on single
5600  * call at a time strategies.
5601  */
5602  break;
5603  }
5604  if (o->block_connected_update) {
5605  ast_verb(3, "Redirecting update to %s prevented\n",
5606  inchan_name);
5607  break;
5608  }
5609  ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
5610  ochan_name, inchan_name);
5611  if (ast_channel_redirecting_sub(o->chan, in, f, 1)) {
5613  }
5614  break;
5617  break;
5618  default:
5619  ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
5620  break;
5621  }
5622  }
5623  ast_frfree(f);
5624  } else { /* ast_read() returned NULL */
5625  endtime = (long) time(NULL) - starttime;
5626  ast_channel_publish_dial(qe->chan, o->chan, on, "NOANSWER");
5627  rna(endtime * 1000, qe, o->chan, on, membername, 1);
5628  do_hang(o);
5629  if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
5630  if (qe->parent->timeoutrestart) {
5631  start_time_tv = ast_tvnow();
5632  }
5633  if (ast_remaining_ms(start_time_tv, orig) > 500) {
5634  ring_one(qe, outgoing, &numbusies);
5635  starttime = (long) time(NULL);
5636  }
5637  }
5638  }
5639  }
5640  }
5641 
5642  /* If we received an event from the caller, deal with it. */
5643  if (winner == in) {
5644  f = ast_read(in);
5645  if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
5646  /* Got hung up */
5647  *to = -1;
5648  if (f) {
5649  if (f->data.uint32) {
5650  ast_channel_hangupcause_set(in, f->data.uint32);
5651  }
5652  ast_frfree(f);
5653  }
5654  canceled_by_caller = 1;
5655  } else if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
5656  ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
5657  *to = 0;
5658  ast_frfree(f);
5659  canceled_by_caller = 1;
5660  } else if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
5661  ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
5662  *to = 0;
5663  *digit = f->subclass.integer;
5664  ast_frfree(f);
5665  canceled_by_caller = 1;
5666  }
5667  /* When caller hung up or pressed * or digit. */
5668  if (canceled_by_caller) {
5669  publish_dial_end_event(in, outgoing, NULL, "CANCEL");
5670  for (o = start; o; o = o->call_next) {
5671  if (o->chan) {
5672  ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), o->member->membername, "RINGCANCELED", "%d", (int) ast_tvdiff_ms(ast_tvnow(), start_time_tv));
5673  }
5674  }
5675  return NULL;
5676  }
5677 
5678  /* Send the frame from the in channel to all outgoing channels. */
5679  for (o = start; o; o = o->call_next) {
5680  if (!o->stillgoing || !o->chan) {
5681  /* This outgoing channel has died so don't send the frame to it. */
5682  continue;
5683  }
5684  switch (f->frametype) {
5685  case AST_FRAME_CONTROL:
5686  switch (f->subclass.integer) {
5688  if (o->block_connected_update) {
5689  ast_verb(3, "Connected line update to %s prevented.\n", ast_channel_name(o->chan));
5690  break;
5691  }
5692  if (ast_channel_connected_line_sub(in, o->chan, f, 1)) {
5693  ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
5694  }
5695  break;
5697  if (o->block_connected_update) {
5698  ast_verb(3, "Redirecting update to %s prevented.\n", ast_channel_name(o->chan));
5699  break;
5700  }
5701  if (ast_channel_redirecting_sub(in, o->chan, f, 1)) {
5702  ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
5703  }
5704  break;
5705  default:
5706  /* We are not going to do anything with this frame. */
5707  goto skip_frame;
5708  }
5709  break;
5710  default:
5711  /* We are not going to do anything with this frame. */
5712  goto skip_frame;
5713  }
5714  }
5715 skip_frame:;
5716 
5717  ast_frfree(f);
5718  }
5719  }
5720 
5721  if (!*to) {
5722  for (o = start; o; o = o->call_next) {
5723  if (o->chan) {
5724  rna(orig, qe, o->chan, o->interface, o->member->membername, 1);
5725  }
5726  }
5727 
5728  publish_dial_end_event(qe->chan, outgoing, NULL, "NOANSWER");
5729  }
5730 
5731  return peer;
5732 }
struct ast_channel * ast_waitfor_n(struct ast_channel **chan, int n, int *ms)
Waits for input on a group of channels Wait for input on an array of channels for a given # of millis...
Definition: channel.c:3157
struct call_queue * parent
Definition: app_queue.c:1693
Main Channel structure associated with a channel.
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
struct ast_channel * chan
Definition: app_queue.c:1720
void ast_party_connected_line_set_init(struct ast_party_connected_line *init, const struct ast_party_connected_line *guide)
Initialize the given connected line structure using the given guide for a set update operation...
Definition: channel.c:2045
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition: channel.h:527
void ast_channel_publish_dial(struct ast_channel *caller, struct ast_channel *peer, const char *dialstring, const char *dialstatus)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
void ast_channel_update_redirecting(struct ast_channel *chan, const struct ast_party_redirecting *redirecting, const struct ast_set_party_redirecting *update)
Indicate that the redirecting id has changed.
Definition: channel.c:10284
void ast_channel_update_connected_line(struct ast_channel *chan, const struct ast_party_connected_line *connected, const struct ast_set_party_connected_line *update)
Indicate that the connected line information has changed.
Definition: channel.c:9093
int ast_call(struct ast_channel *chan, const char *addr, int timeout)
Make a call.
Definition: channel.c:6461
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4257
void * ast_aoc_destroy_decoded(struct ast_aoc_decoded *decoded)
free an ast_aoc_decoded object
Definition: aoc.c:307
ast_channel_state
ast_channel states
Definition: channelstate.h:35
int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, size_t datalen)
Indicates condition of channel, with payload.
Definition: channel.c:4653
char interface[256]
Definition: app_queue.c:1673
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition: time.h:107
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
void ast_party_connected_line_free(struct ast_party_connected_line *doomed)
Destroy the connected line information contents.
Definition: channel.c:2072
void ast_moh_stop(struct ast_channel *chan)
Turn off music on hold on a given channel.
Definition: channel.c:7776
#define AST_MAX_WATCHERS
Maximum number of channels we can watch at a time.
Definition: dial.c:211
char membername[80]
Definition: app_queue.c:1732
char interface[AST_CHANNEL_NAME]
Definition: app_queue.c:1727
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
struct ast_frame_subclass subclass
struct ast_channel * ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
Requests a channel.
Definition: channel.c:6354
const struct ast_channel_tech * tech
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
int ast_channel_datastore_inherit(struct ast_channel *from, struct ast_channel *to)
Inherit datastores from a parent to a child.
Definition: channel.c:2368
struct ast_party_connected_line connected
Definition: app_queue.c:1677
struct ast_aoc_decoded * ast_aoc_decode(struct ast_aoc_encoded *encoded, size_t size, struct ast_channel *chan)
decodes an encoded aoc payload.
Definition: aoc.c:449
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
struct ast_aoc_encoded * ast_aoc_encode(struct ast_aoc_decoded *decoded, size_t *out_size, struct ast_channel *chan)
encodes a decoded aoc structure so it can be passed on the wire
Definition: aoc.c:650
unsigned int block_connected_update
Definition: app_queue.c:1681
int ring_when_ringing
Definition: app_queue.c:1703
int ast_channel_redirecting_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *redirecting_info, int is_frame)
Run a redirecting interception subroutine and update a channel's redirecting information.
Definition: channel.c:10383
void ast_party_number_init(struct ast_party_number *init)
Initialize the given number structure.
Definition: channel.c:1644
#define ast_debug(level,...)
Log a DEBUG message.
static void rna(int rnatime, struct queue_ent *qe, struct ast_channel *peer, 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...
Definition: app_queue.c:5110
void ast_channel_req_accountcodes(struct ast_channel *chan, const struct ast_channel *requestor, enum ast_channel_requestor_relationship relationship)
Setup new channel accountcodes from the requestor channel after ast_request().
Definition: channel.c:6434
void ast_party_number_free(struct ast_party_number *doomed)
Destroy the party number contents.
Definition: channel.c:1691
We define a custom "local user" structure because we use it not only for keeping track of what is in ...
Definition: app_queue.c:1669
int ast_connected_line_parse_data(const unsigned char *data, size_t datalen, struct ast_party_connected_line *connected)
Parse connected line indication frame data.
Definition: channel.c:8785
char * orig_chan_name
Definition: app_queue.c:1688
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: utils.c:2281
int ast_channel_connected_line_sub(struct ast_channel *autoservice_chan, struct ast_channel *sub_chan, const void *connected_info, int frame)
Run a connected line interception subroutine and update a channel's connected line information...
Definition: channel.c:10338
const ast_string_field name
Definition: app_queue.c:1829
void ast_party_connected_line_set(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src, const struct ast_set_party_connected_line *update)
Set the connected line information based on another connected line source.
Definition: channel.c:2054
Connected Line/Party information.
Definition: channel.h:456
unsigned int pending_connected_update
Definition: app_queue.c:1679
Redirecting Line information. RDNIS (Redirecting Directory Number Information Service) Where a call d...
Definition: channel.h:522
void ast_channel_publish_dial_forward(struct ast_channel *caller, struct ast_channel *peer, struct ast_channel *forwarded, const char *dialstring, const char *dialstatus, const char *forward)
Publish in the ast_channel_topic or ast_channel_topic_all topics a stasis message for the channels in...
union ast_frame::@224 data
void ast_channel_inherit_variables(const struct ast_channel *parent, struct ast_channel *child)
Inherits channel variable from parent to child channel.
Definition: channel.c:6771
#define AST_CHANNEL_NAME
Definition: channel.h:171
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
void * ast_aoc_destroy_encoded(struct ast_aoc_encoded *encoded)
free an ast_aoc_encoded object
Definition: aoc.c:313
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
#define ast_channel_lock_both(chan1, chan2)
Lock two channels.
Definition: channel.h:2929
int transit_network_select
Transit Network Select.
Definition: channel.h:397
void ast_party_redirecting_free(struct ast_party_redirecting *doomed)
Destroy the redirecting information contents.
Definition: channel.c:2179
void ast_party_connected_line_copy(struct ast_party_connected_line *dest, const struct ast_party_connected_line *src)
Copy the source connected line information to the destination connected line.
Definition: channel.c:2031
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
void ast_connected_line_copy_from_caller(struct ast_party_connected_line *dest, const struct ast_party_caller *src)
Copy the caller information to the connected line information.
Definition: channel.c:8293
void ast_party_redirecting_init(struct ast_party_redirecting *init)
Initialize the given redirecting structure.
Definition: channel.c:2122
Data structure associated with a single frame of data.
enum ast_aoc_type ast_aoc_get_msg_type(struct ast_aoc_decoded *decoded)
get the message type, AOC-D, AOC-E, or AOC Request
Definition: aoc.c:892
enum ast_frame_type frametype
static void do_hang(struct callattempt *o)
common hangup actions
Definition: app_queue.c:4627
unsigned int dial_callerid_absent
Definition: app_queue.c:1683
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
Definition: app_queue.c:4172
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:297
unsigned int stillgoing
Definition: app_queue.c:1685
void ast_party_redirecting_copy(struct ast_party_redirecting *dest, const struct ast_party_redirecting *src)
Copy the source redirecting information to the destination redirecting.
Definition: channel.c:2135
static int ring_one(struct queue_ent *qe, struct callattempt *outgoing, int *busies)
Place a call to a queue member.
Definition: app_queue.c:4910
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
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 5884 of file app_queue.c.

References call_queue::announcefrequency, ast_waitfordigit(), queue_ent::chan, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), 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_ent::raise_penalty, RECHECK, record_abandoned(), say_periodic_announcement(), queue_ent::start, penalty_rule::time, update_qe_rule(), valid_exit(), and queue_ent::withdraw.

Referenced by queue_exec().

5885 {
5886  int res = 0;
5887 
5888  /* This is the holding pen for callers 2 through maxlen */
5889  for (;;) {
5890 
5891  /* A request to withdraw this call from the queue arrived */
5892  if (qe->withdraw) {
5893  *reason = QUEUE_WITHDRAW;
5894  res = 1;
5895  break;
5896  }
5897 
5898  if (is_our_turn(qe)) {
5899  break;
5900  }
5901 
5902  /* If we have timed out, break out */
5903  if (qe->expire && (time(NULL) >= qe->expire)) {
5904  *reason = QUEUE_TIMEOUT;
5905  break;
5906  }
5907 
5908  if (qe->parent->leavewhenempty) {
5909  int status = 0;
5910 
5911  if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->raise_penalty, qe->parent->leavewhenempty, 0))) {
5912  record_abandoned(qe);
5913  *reason = QUEUE_LEAVEEMPTY;
5914  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));
5915  res = -1;
5916  qe->handled = -1;
5917  break;
5918  }
5919  }
5920 
5921  /* Make a position announcement, if enabled */
5922  if (qe->parent->announcefrequency &&
5923  (res = say_position(qe,ringing))) {
5924  break;
5925  }
5926 
5927  /* If we have timed out, break out */
5928  if (qe->expire && (time(NULL) >= qe->expire)) {
5929  *reason = QUEUE_TIMEOUT;
5930  break;
5931  }
5932 
5933  /* Make a periodic announcement, if enabled */
5934  if (qe->parent->periodicannouncefrequency &&
5935  (res = say_periodic_announcement(qe,ringing)))
5936  break;
5937 
5938  /* see if we need to move to the next penalty level for this queue */
5939  while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
5940  update_qe_rule(qe);
5941  }
5942 
5943  /* If we have timed out, break out */
5944  if (qe->expire && (time(NULL) >= qe->expire)) {
5945  *reason = QUEUE_TIMEOUT;
5946  break;
5947  }
5948 
5949  /* Wait a second before checking again */
5950  if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
5951  if (res > 0 && !valid_exit(qe, res)) {
5952  res = 0;
5953  } else {
5954  break;
5955  }
5956  }
5957 
5958  /* If we have timed out, break out */
5959  if (qe->expire && (time(NULL) >= qe->expire)) {
5960  *reason = QUEUE_TIMEOUT;
5961  break;
5962  }
5963  }
5964 
5965  return res;
5966 }
struct call_queue * parent
Definition: app_queue.c:1693
int raise_penalty
Definition: app_queue.c:1712
struct ast_channel * chan
Definition: app_queue.c:1720
int min_penalty
Definition: app_queue.c:1711
int max_penalty
Definition: app_queue.c:1710
time_t start
Definition: app_queue.c:1715
time_t expire
Definition: app_queue.c:1716
static int say_periodic_announcement(struct queue_ent *qe, int ringing)
Playback announcement to queued members if period has elapsed.
Definition: app_queue.c:5018
int periodicannouncefrequency
Definition: app_queue.c:1856
static void ringing(struct ast_channel *chan)
Helper method to send a ringing indication to a channel in a bridge.
const ast_string_field name
Definition: app_queue.c:1829
struct penalty_rule * pr
Definition: app_queue.c:1722
unsigned int withdraw
Definition: app_queue.c:1718
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3175
static void record_abandoned(struct queue_ent *qe)
Record that a caller gave up on waiting in queue.
Definition: app_queue.c:5079
static int get_member_status(struct call_queue *q, int max_penalty, int min_penalty, int raise_penalty, enum empty_conditions conditions, int devstate)
Check if members are available.
Definition: app_queue.c:2422
#define RECHECK
Definition: app_queue.c:1555
int handled
Definition: app_queue.c:1708
static void update_qe_rule(struct queue_ent *qe)
update rules for queues
Definition: app_queue.c:5794
static int is_our_turn(struct queue_ent *qe)
Check if we should start attempting to call queue members.
Definition: app_queue.c:5745
static int valid_exit(struct queue_ent *qe, char digit)
Check for valid exit from queue via goto.
Definition: app_queue.c:4172
int announcefrequency
Definition: app_queue.c:1853
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.
Return values
1if the word is found
0if the word is not found

Definition at line 10250 of file app_queue.c.

Referenced by complete_queue().

10250  {
10251  int list_len, word_len = strlen(word);
10252  const char *find, *end_find, *end_list;
10253 
10254  /* strip whitespace from front */
10255  while(isspace(*list)) {
10256  list++;
10257  }
10258 
10259  while((find = strstr(list, word))) {
10260  /* beginning of find starts inside another word? */
10261  if (find != list && *(find - 1) != ' ') {
10262  list = find;
10263  /* strip word from front */
10264  while(!isspace(*list) && *list != '\0') {
10265  list++;
10266  }
10267  /* strip whitespace from front */
10268  while(isspace(*list)) {
10269  list++;
10270  }
10271  continue;
10272  }
10273 
10274  /* end of find ends inside another word or at very end of list? */
10275  list_len = strlen(list);
10276  end_find = find + word_len;
10277  end_list = list + list_len;
10278  if (end_find == end_list || *end_find != ' ') {
10279  list = find;
10280  /* strip word from front */
10281  while(!isspace(*list) && *list != '\0') {
10282  list++;
10283  }
10284  /* strip whitespace from front */
10285  while(isspace(*list)) {
10286  list++;
10287  }
10288  continue;
10289  }
10290 
10291  /* terminating conditions satisfied, word at beginning or separated by ' ' */
10292  return 1;
10293  }
10294 
10295  return 0;
10296 }

Variable Documentation

const struct autopause autopausesmodes[]
static
Initial value:
= {
{ QUEUE_AUTOPAUSE_OFF,"no" },
{ QUEUE_AUTOPAUSE_ON, "yes" },
{ QUEUE_AUTOPAUSE_ALL,"all" },
}
struct ast_custom_function queueexists_function
static
Initial value:
= {
.name = "QUEUE_EXISTS",
}
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.
Definition: app_queue.c:8968

Definition at line 9448 of file app_queue.c.

struct ast_custom_function queuegetchannel_function
static
Initial value:
= {
.name = "QUEUE_GET_CHANNEL",
}
static int queue_function_queuegetchannel(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Dialplan function QUEUE_GET_CHANNEL() Get caller channel waiting at specified position in the queue...
Definition: app_queue.c:9216

Definition at line 9469 of file app_queue.c.

struct ast_custom_function queuemembercount_dep
static
Initial value:
= {
.name = "QUEUE_MEMBER_COUNT",
}
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)
Definition: app_queue.c:9175

Definition at line 9464 of file app_queue.c.

struct ast_custom_function queuemembercount_function
static
Initial value:
= {
.name = "QUEUE_MEMBER",
}
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.
Definition: app_queue.c:9112
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.
Definition: app_queue.c:9010

Definition at line 9458 of file app_queue.c.

struct ast_custom_function queuememberlist_function
static
Initial value:
= {
.name = "QUEUE_MEMBER_LIST",
}
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.
Definition: app_queue.c:9332

Definition at line 9479 of file app_queue.c.

struct ast_custom_function queuememberpenalty_function
static
Initial value:
= {
.name = "QUEUE_MEMBER_PENALTY",
}
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.
Definition: app_queue.c:9380
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.
Definition: app_queue.c:9412

Definition at line 9484 of file app_queue.c.

struct ast_custom_function queuevar_function
static
Initial value:
= {
.name = "QUEUE_VARIABLES",
}
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.
Definition: app_queue.c:8924

Definition at line 9453 of file app_queue.c.

struct ast_custom_function queuewaitingcount_function
static
Initial value:
= {
.name = "QUEUE_WAITING_COUNT",
}
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.
Definition: app_queue.c:9295

Definition at line 9474 of file app_queue.c.