Mon Aug 31 12:30:16 2015

Asterisk developer's documentation


app_queue.c File Reference

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

#include "asterisk.h"
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <ctype.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/event.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/global_datastores.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/aoc.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"

Go to the source code of this file.

Data Structures

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

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define ANNOUNCEPOSITION_LIMIT   4
#define ANNOUNCEPOSITION_MORE_THAN   3
#define ANNOUNCEPOSITION_NO   2
#define ANNOUNCEPOSITION_YES   1
#define AST_MAX_WATCHERS   256
#define DATA_EXPORT_CALL_QUEUE(MEMBER)
#define DATA_EXPORT_MEMBER(MEMBER)
#define DATA_EXPORT_QUEUE_ENT(MEMBER)
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define MAX_PERIODIC_ANNOUNCEMENTS   10
#define MAX_QUEUE_BUCKETS   53
#define QUEUE_EVENT_VARIABLES   3
#define queue_t_ref(a, b)   queue_ref(a)
#define queue_t_unref(a, b)   queue_unref(a)
#define queues_t_link(c, q, tag)   ao2_t_link(c,q,tag)
#define queues_t_unlink(c, q, tag)   ao2_t_unlink(c,q,tag)
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM,
  QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM, QUEUE_STRATEGY_RRORDERED
}
enum  { QUEUE_AUTOPAUSE_OFF = 0, QUEUE_AUTOPAUSE_ON, QUEUE_AUTOPAUSE_ALL }
enum  agent_complete_reason { CALLER, AGENT, TRANSFER }
enum  empty_conditions {
  QUEUE_EMPTY_PENALTY = (1 << 0), QUEUE_EMPTY_PAUSED = (1 << 1), QUEUE_EMPTY_INUSE = (1 << 2), QUEUE_EMPTY_RINGING = (1 << 3),
  QUEUE_EMPTY_UNAVAILABLE = (1 << 4), QUEUE_EMPTY_INVALID = (1 << 5), QUEUE_EMPTY_UNKNOWN = (1 << 6), QUEUE_EMPTY_WRAPUP = (1 << 7)
}
enum  queue_reload_mask { QUEUE_RELOAD_PARAMETERS = (1 << 0), QUEUE_RELOAD_MEMBER = (1 << 1), QUEUE_RELOAD_RULES = (1 << 2), QUEUE_RESET_STATS = (1 << 3) }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7
}
enum  queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF }

Functions

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

Variables

static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_pqm = "PauseQueueMember"
static char * app_ql = "QueueLog"
static char * app_rqm = "RemoveQueueMember"
static char * app_upqm = "UnpauseQueueMember"
static int autofill_default = 1
 queues.conf [general] option
static struct autopause autopausesmodes []
static struct ast_cli_entry cli_queue []
static struct ast_event_subdevice_state_sub
 Subscription to device state change events.
static struct ast_taskprocessordevicestate_tps
static int montype_default = 0
 queues.conf [general] option
static const char *const pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static struct ast_data_entry queue_data_providers []
static int queue_debug = 0
 queues.conf [general] extra debug option
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queueexists_function
static struct ast_custom_function queuemembercount_dep
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpaused_function
static struct ast_custom_function queuememberpenalty_function
static struct ast_custom_function queuememberstatus_function
static struct ao2_containerqueues
static struct ast_data_handler queues_data_provider
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 1
 queues.conf [general] option
static struct strategy strategies []
static int update_cdr = 0
 queues.conf [general] option
static int use_weight = 0
 queues.conf per-queue weight option

Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>

Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
These features added by David C. Troy <dave@toad.net>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 1204 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 1205 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_LIMIT   4

We not announce position more than <limit>

Definition at line 1220 of file app_queue.c.

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

#define ANNOUNCEPOSITION_MORE_THAN   3

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

Definition at line 1219 of file app_queue.c.

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

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 1218 of file app_queue.c.

Referenced by queue_set_param(), and queues_data_provider_get_helper().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 1217 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

Definition at line 3771 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 8791 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8856 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

Definition at line 8870 of file app_queue.c.

#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15

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

Definition at line 1025 of file app_queue.c.

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 1021 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 1022 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

The maximum periodic announcements we can have

Definition at line 1024 of file app_queue.c.

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 1027 of file app_queue.c.

Referenced by load_module().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 1206 of file app_queue.c.

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

#define queue_t_ref ( a,
 )     queue_ref(a)

Definition at line 1468 of file app_queue.c.

Referenced by leave_queue(), and try_calling().

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

Definition at line 1470 of file app_queue.c.

Referenced by find_queue_by_name_rt(), and reload_single_queue().

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

Definition at line 1471 of file app_queue.c.

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

#define RECHECK   1

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

Definition at line 1023 of file app_queue.c.

Referenced by wait_our_turn().

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

Enumeration Type Documentation

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

Definition at line 970 of file app_queue.c.

anonymous enum
Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 981 of file app_queue.c.

00981      {
00982      QUEUE_AUTOPAUSE_OFF = 0,
00983      QUEUE_AUTOPAUSE_ON,
00984      QUEUE_AUTOPAUSE_ALL
00985 };

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4626 of file app_queue.c.

04626                            {
04627    CALLER,
04628    AGENT,
04629    TRANSFER
04630 };

Enumerator:
QUEUE_EMPTY_PENALTY 
QUEUE_EMPTY_PAUSED 
QUEUE_EMPTY_INUSE 
QUEUE_EMPTY_RINGING 
QUEUE_EMPTY_UNAVAILABLE 
QUEUE_EMPTY_INVALID 
QUEUE_EMPTY_UNKNOWN 
QUEUE_EMPTY_WRAPUP 

Definition at line 1192 of file app_queue.c.

01192                       {
01193    QUEUE_EMPTY_PENALTY = (1 << 0),
01194    QUEUE_EMPTY_PAUSED = (1 << 1),
01195    QUEUE_EMPTY_INUSE = (1 << 2),
01196    QUEUE_EMPTY_RINGING = (1 << 3),
01197    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
01198    QUEUE_EMPTY_INVALID = (1 << 5),
01199    QUEUE_EMPTY_UNKNOWN = (1 << 6),
01200    QUEUE_EMPTY_WRAPUP = (1 << 7),
01201 };

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 987 of file app_queue.c.

00987                        {
00988    QUEUE_RELOAD_PARAMETERS = (1 << 0),
00989    QUEUE_RELOAD_MEMBER = (1 << 1),
00990    QUEUE_RELOAD_RULES = (1 << 2),
00991    QUEUE_RESET_STATS = (1 << 3),
00992 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 1074 of file app_queue.c.

01074                   {
01075    QUEUE_UNKNOWN = 0,
01076    QUEUE_TIMEOUT = 1,
01077    QUEUE_JOINEMPTY = 2,
01078    QUEUE_LEAVEEMPTY = 3,
01079    QUEUE_JOINUNAVAIL = 4,
01080    QUEUE_LEAVEUNAVAIL = 5,
01081    QUEUE_FULL = 6,
01082    QUEUE_CONTINUE = 7,
01083 };

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 1099 of file app_queue.c.

01099                             {
01100    TIMEOUT_PRIORITY_APP,
01101    TIMEOUT_PRIORITY_CONF,
01102 };


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

References ao2_container_count(), ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_devstate2str(), ast_load_realtime_multientry(), ast_str_alloca, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_strlen_zero(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, CLI_SHOWUSAGE, CLI_SUCCESS, call_queue::count, do_print(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_t_unref, queues, member::realtime, call_queue::realtime, call_queue::ringlimit, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

07590 {
07591    struct call_queue *q;
07592    struct ast_str *out = ast_str_alloca(240);
07593    int found = 0;
07594    time_t now = time(NULL);
07595    struct ao2_iterator queue_iter;
07596    struct ao2_iterator mem_iter;
07597 
07598    if (argc != 2 && argc != 3)
07599       return CLI_SHOWUSAGE;
07600 
07601    if (argc == 3) { /* specific queue */
07602       if ((q = load_realtime_queue(argv[2]))) {
07603          queue_t_unref(q, "Done with temporary pointer");
07604       }
07605    } else if (ast_check_realtime("queues")) {
07606       /* This block is to find any queues which are defined in realtime but
07607        * which have not yet been added to the in-core container
07608        */
07609       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
07610       char *queuename;
07611       if (cfg) {
07612          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
07613             if ((q = load_realtime_queue(queuename))) {
07614                queue_t_unref(q, "Done with temporary pointer");
07615             }
07616          }
07617          ast_config_destroy(cfg);
07618       }
07619    }
07620 
07621    ao2_lock(queues);
07622    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
07623    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07624       float sl;
07625       struct call_queue *realtime_queue = NULL;
07626 
07627       ao2_lock(q);
07628       /* This check is to make sure we don't print information for realtime
07629        * queues which have been deleted from realtime but which have not yet
07630        * been deleted from the in-core container. Only do this if we're not
07631        * looking for a specific queue.
07632        */
07633       if (argc < 3 && q->realtime) {
07634          realtime_queue = load_realtime_queue(q->name);
07635          if (!realtime_queue) {
07636             ao2_unlock(q);
07637             queue_t_unref(q, "Done with iterator");
07638             continue;
07639          }
07640          queue_t_unref(realtime_queue, "Queue is already in memory");
07641       }
07642 
07643       if (argc == 3 && strcasecmp(q->name, argv[2])) {
07644          ao2_unlock(q);
07645          queue_t_unref(q, "Done with iterator");
07646          continue;
07647       }
07648       found = 1;
07649 
07650       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
07651       if (q->maxlen)
07652          ast_str_append(&out, 0, "%d", q->maxlen);
07653       else
07654          ast_str_append(&out, 0, "unlimited");
07655       sl = 0;
07656       if (q->callscompleted > 0)
07657          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
07658       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
07659          int2strat(q->strategy), q->holdtime, q->talktime, q->ringlimit, q->weight,
07660          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
07661       do_print(s, fd, ast_str_buffer(out));
07662       if (!ao2_container_count(q->members))
07663          do_print(s, fd, "   No Members");
07664       else {
07665          struct member *mem;
07666 
07667          do_print(s, fd, "   Members: ");
07668          mem_iter = ao2_iterator_init(q->members, 0);
07669          while ((mem = ao2_iterator_next(&mem_iter))) {
07670             ast_str_set(&out, 0, "      %s", mem->membername);
07671             if (strcasecmp(mem->membername, mem->interface)) {
07672                ast_str_append(&out, 0, " (%s)", mem->interface);
07673             }
07674             if (mem->penalty)
07675                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
07676             ast_str_append(&out, 0, "%s%s%s (%s)",
07677                mem->dynamic ? " (dynamic)" : "",
07678                mem->realtime ? " (realtime)" : "",
07679                mem->paused ? " (paused)" : "",
07680                ast_devstate2str(mem->status));
07681             if (mem->calls)
07682                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
07683                   mem->calls, (long) (time(NULL) - mem->lastcall));
07684             else
07685                ast_str_append(&out, 0, " has taken no calls yet");
07686             do_print(s, fd, ast_str_buffer(out));
07687             ao2_ref(mem, -1);
07688          }
07689          ao2_iterator_destroy(&mem_iter);
07690       }
07691       if (!q->head)
07692          do_print(s, fd, "   No Callers");
07693       else {
07694          struct queue_ent *qe;
07695          int pos = 1;
07696 
07697          do_print(s, fd, "   Callers: ");
07698          for (qe = q->head; qe; qe = qe->next) {
07699             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
07700                pos++, qe->chan->name, (long) (now - qe->start) / 60,
07701                (long) (now - qe->start) % 60, qe->prio);
07702             do_print(s, fd, ast_str_buffer(out));
07703          }
07704       }
07705       do_print(s, fd, ""); /* blank line between entries */
07706       ao2_unlock(q);
07707       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
07708    }
07709    ao2_iterator_destroy(&queue_iter);
07710    ao2_unlock(queues);
07711    if (!found) {
07712       if (argc == 3)
07713          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07714       else
07715          ast_str_set(&out, 0, "No queues.");
07716       do_print(s, fd, ast_str_buffer(out));
07717    }
07718    return CLI_SUCCESS;
07719 }

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

Add member to queue.

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

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

Definition at line 5759 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), manager_event, member_add_to_queue(), member::membername, member::paused, member::penalty, queue_t_unref, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and member::status.

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

05760 {
05761    struct call_queue *q;
05762    struct member *new_member, *old_member;
05763    int res = RES_NOSUCHQUEUE;
05764 
05765    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05766     * short-circuits if the queue is already in memory. */
05767    if (!(q = load_realtime_queue(queuename)))
05768       return res;
05769 
05770    ao2_lock(q);
05771    if ((old_member = interface_exists(q, interface)) == NULL) {
05772       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05773          new_member->dynamic = 1;
05774          member_add_to_queue(q, new_member);
05775          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05776             "Queue: %s\r\n"
05777             "Location: %s\r\n"
05778             "MemberName: %s\r\n"
05779             "Membership: %s\r\n"
05780             "Penalty: %d\r\n"
05781             "CallsTaken: %d\r\n"
05782             "LastCall: %d\r\n"
05783             "Status: %d\r\n"
05784             "Paused: %d\r\n",
05785             q->name, new_member->interface, new_member->membername,
05786             "dynamic",
05787             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05788             new_member->status, new_member->paused);
05789          
05790          ao2_ref(new_member, -1);
05791          new_member = NULL;
05792 
05793          if (dump)
05794             dump_queue_members(q);
05795          
05796          res = RES_OKAY;
05797       } else {
05798          res = RES_OUTOFMEMORY;
05799       }
05800    } else {
05801       ao2_ref(old_member, -1);
05802       res = RES_EXISTS;
05803    }
05804    ao2_unlock(q);
05805    queue_t_unref(q, "Expiring temporary reference");
05806 
05807    return res;
05808 }

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

Definition at line 2414 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_single_queue().

02415 {
02416    struct call_queue *q;
02417 
02418    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02419       if (ast_string_field_init(q, 64)) {
02420          queue_t_unref(q, "String field allocation failed");
02421          return NULL;
02422       }
02423       ast_string_field_set(q, name, queuename);
02424    }
02425    return q;
02426 }

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

AddQueueMember application.

Definition at line 6191 of file app_queue.c.

References add_to_queue(), args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

06192 {
06193    int res=-1;
06194    char *parse, *temppos = NULL;
06195    AST_DECLARE_APP_ARGS(args,
06196       AST_APP_ARG(queuename);
06197       AST_APP_ARG(interface);
06198       AST_APP_ARG(penalty);
06199       AST_APP_ARG(options);
06200       AST_APP_ARG(membername);
06201       AST_APP_ARG(state_interface);
06202    );
06203    int penalty = 0;
06204 
06205    if (ast_strlen_zero(data)) {
06206       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
06207       return -1;
06208    }
06209 
06210    parse = ast_strdupa(data);
06211 
06212    AST_STANDARD_APP_ARGS(args, parse);
06213 
06214    if (ast_strlen_zero(args.interface)) {
06215       args.interface = ast_strdupa(chan->name);
06216       temppos = strrchr(args.interface, '-');
06217       if (temppos)
06218          *temppos = '\0';
06219    }
06220 
06221    if (!ast_strlen_zero(args.penalty)) {
06222       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
06223          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
06224          penalty = 0;
06225       }
06226    }
06227 
06228    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
06229    case RES_OKAY:
06230       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
06231       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
06232       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
06233       res = 0;
06234       break;
06235    case RES_EXISTS:
06236       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
06237       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
06238       res = 0;
06239       break;
06240    case RES_NOSUCHQUEUE:
06241       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
06242       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
06243       res = 0;
06244       break;
06245    case RES_OUTOFMEMORY:
06246       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
06247       break;
06248    }
06249 
06250    return res;
06251 }

AST_DATA_STRUCTURE ( queue_ent  ,
DATA_EXPORT_QUEUE_ENT   
)
AST_DATA_STRUCTURE ( member  ,
DATA_EXPORT_MEMBER   
)
AST_DATA_STRUCTURE ( call_queue  ,
DATA_EXPORT_CALL_QUEUE   
)
static AST_LIST_HEAD_STATIC ( rule_lists  ,
rule_list   
) [static]
AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_LOAD_ORDER  ,
"True Call Queueing"  ,
load = load_module,
unload = unload_module,
reload = reload,
load_pri = AST_MODPRI_DEVSTATE_CONSUMER,
nonoptreq = "res_monitor" 
)
static int attended_transfer_occurred ( struct ast_channel chan  )  [static]

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

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

Note:
Only call this with chan locked

Definition at line 4731 of file app_queue.c.

References ast_channel_datastore_find().

Referenced by try_calling().

04732 {
04733    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04734 }

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

Definition at line 1373 of file app_queue.c.

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

Referenced by queue_set_param().

01374 {
01375    int x;
01376    /*This 'double check' that default value is OFF */
01377    if (ast_strlen_zero(autopause))
01378       return QUEUE_AUTOPAUSE_OFF;
01379 
01380    /*This 'double check' is to ensure old values works */
01381    if(ast_true(autopause))
01382       return QUEUE_AUTOPAUSE_ON;
01383 
01384    for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
01385       if (!strcasecmp(autopause, autopausesmodes[x].name))
01386          return autopausesmodes[x].autopause;
01387    }
01388 
01389    /*This 'double check' that default value is OFF */
01390    return QUEUE_AUTOPAUSE_OFF;
01391 }

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

Calculate the metric of each member in the outgoing callattempts.

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

Return values:
-1 if penalties are exceeded
0 otherwise

Definition at line 4550 of file app_queue.c.

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

Referenced by try_calling().

04551 {
04552    /* disregarding penalty on too few members? */
04553    int membercount = ao2_container_count(q->members);
04554    unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04555 
04556    if (usepenalty) {
04557       if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) ||
04558          (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) {
04559          return -1;
04560       }
04561    } else {
04562       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04563            membercount, q->penaltymemberslimit);
04564    }
04565 
04566    switch (q->strategy) {
04567    case QUEUE_STRATEGY_RINGALL:
04568       /* Everyone equal, except for penalty */
04569       tmp->metric = mem->penalty * 1000000 * usepenalty;
04570       break;
04571    case QUEUE_STRATEGY_LINEAR:
04572       if (pos < qe->linpos) {
04573          tmp->metric = 1000 + pos;
04574       } else {
04575          if (pos > qe->linpos)
04576             /* Indicate there is another priority */
04577             qe->linwrapped = 1;
04578          tmp->metric = pos;
04579       }
04580       tmp->metric += mem->penalty * 1000000 * usepenalty;
04581       break;
04582    case QUEUE_STRATEGY_RRORDERED:
04583    case QUEUE_STRATEGY_RRMEMORY:
04584       pos = mem->queuepos;
04585       if (pos < q->rrpos) {
04586          tmp->metric = 1000 + pos;
04587       } else {
04588          if (pos > q->rrpos)
04589             /* Indicate there is another priority */
04590             q->wrapped = 1;
04591          tmp->metric = pos;
04592       }
04593       tmp->metric += mem->penalty * 1000000 * usepenalty;
04594       break;
04595    case QUEUE_STRATEGY_RANDOM:
04596       tmp->metric = ast_random() % 1000;
04597       tmp->metric += mem->penalty * 1000000 * usepenalty;
04598       break;
04599    case QUEUE_STRATEGY_WRANDOM:
04600       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04601       break;
04602    case QUEUE_STRATEGY_FEWESTCALLS:
04603       tmp->metric = mem->calls;
04604       tmp->metric += mem->penalty * 1000000 * usepenalty;
04605       break;
04606    case QUEUE_STRATEGY_LEASTRECENT:
04607       if (!mem->lastcall)
04608          tmp->metric = 0;
04609       else
04610          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04611       tmp->metric += mem->penalty * 1000000 * usepenalty;
04612       break;
04613    default:
04614       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04615       break;
04616    }
04617    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
04618       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
04619    }
04620    if (option_debug)
04621       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
04622                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
04623    return 0;
04624 }

static void callattempt_free ( struct callattempt doomed  )  [static]

Definition at line 3076 of file app_queue.c.

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

Referenced by hangupcalls(), and try_calling().

03077 {
03078    if (doomed->member) {
03079       ao2_ref(doomed->member, -1);
03080    }
03081    ast_party_connected_line_free(&doomed->connected);
03082    ast_free(doomed);
03083 }

static int can_ring_entry ( struct queue_ent qe,
struct callattempt call 
) [static]

Definition at line 3296 of file app_queue.c.

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

Referenced by ring_entry().

03297 {
03298    if (call->member->paused) {
03299       if (queue_debug)
03300          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", call->interface);
03301       return 0;
03302    }
03303 
03304    if (!qe->parent->ringinuse && !member_status_available(call->member->status)) {
03305       if (queue_debug)
03306          ast_log(LOG_NOTICE, "%s not available, can't receive call\n", call->interface);
03307       return 0;
03308    }
03309 
03310    if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
03311       || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
03312       if (queue_debug)
03313          ast_log(LOG_NOTICE, "Wrapuptime not yet expired on queue %s for %s\n",
03314                      (call->lastqueue ? call->lastqueue->name : qe->parent->name),
03315                      call->interface);
03316       return 0;
03317    }
03318 
03319    if (use_weight && compare_weight(qe->parent, call->member)) {
03320       if (queue_debug)
03321          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n",
03322                      qe->parent->name, call->interface);
03323       return 0;
03324    }
03325 
03326    if (!qe->parent->ringinuse) {
03327       if (member_call_pending_set(call->member)) {
03328          if (queue_debug)
03329             ast_log(LOG_NOTICE, "%s has another call pending, can't receive call\n",
03330                         call->interface);
03331          return 0;
03332       }
03333 
03334       /*
03335        * The queue member is available.  Get current status to be sure
03336        * because the device state and extension state callbacks may
03337        * not have updated the status yet.
03338        */
03339       if (!member_status_available(get_queue_member_status(call->member))) {
03340          ast_debug(1, "%s actually not available, can't receive call\n",
03341             call->interface);
03342          member_call_pending_clear(call->member);
03343          return 0;
03344       }
03345    }
03346 
03347    return 1;
03348 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 1939 of file app_queue.c.

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

Referenced by clear_stats(), and find_queue_by_name_rt().

01940 {
01941    q->holdtime = 0;
01942    q->callscompleted = 0;
01943    q->callsabandoned = 0;
01944    q->callscompletedinsl = 0;
01945    q->talktime = 0;
01946 
01947    if (q->members) {
01948       struct member *mem;
01949       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01950       while ((mem = ao2_iterator_next(&mem_iter))) {
01951          mem->calls = 0;
01952          mem->lastcall = 0;
01953          ao2_ref(mem, -1);
01954       }
01955       ao2_iterator_destroy(&mem_iter);
01956    }
01957 }

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

Facilitates resetting statistics for a queue.

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

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

Definition at line 7528 of file app_queue.c.

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

Referenced by reload_handler().

07529 {
07530    struct call_queue *q;
07531    struct ao2_iterator queue_iter;
07532 
07533    queue_iter = ao2_iterator_init(queues, 0);
07534    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07535       ao2_lock(q);
07536       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
07537          clear_queue(q);
07538       ao2_unlock(q);
07539       queue_t_unref(q, "Done with iterator");
07540    }
07541    ao2_iterator_destroy(&queue_iter);
07542    return 0;
07543 }

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

Definition at line 3159 of file app_queue.c.

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

Referenced by can_ring_entry().

03160 {
03161    struct call_queue *q;
03162    struct member *mem;
03163    int found = 0;
03164    struct ao2_iterator queue_iter;
03165 
03166    queue_iter = ao2_iterator_init(queues, 0);
03167    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03168       if (q == rq) { /* don't check myself, could deadlock */
03169          queue_t_unref(q, "Done with iterator");
03170          continue;
03171       }
03172       ao2_lock(q);
03173       if (q->count && q->members) {
03174          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
03175             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
03176             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
03177                ast_debug(1, "Queue '%s' (weight %d, calls %d) is preferred over '%s' (weight %d, calls %d)\n", q->name, q->weight, q->count, rq->name, rq->weight, rq->count);
03178                found = 1;
03179             }
03180             ao2_ref(mem, -1);
03181          }
03182       }
03183       ao2_unlock(q);
03184       queue_t_unref(q, "Done with iterator");
03185       if (found) {
03186          break;
03187       }
03188    }
03189    ao2_iterator_destroy(&queue_iter);
03190    return found;
03191 }

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:
line The line as typed not including the current word being completed
word The word currently being completed
pos The number of completed words in line
state The nth desired completion option
word_list_offset Offset 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 7793 of file app_queue.c.

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

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

07794 {
07795    struct call_queue *q;
07796    char *ret = NULL;
07797    int which = 0;
07798    int wordlen = strlen(word);
07799    struct ao2_iterator queue_iter;
07800    const char *word_list = NULL;
07801 
07802    /* for certain commands, already completed items should be left out of
07803     * the list */
07804    if (word_list_offset && strlen(line) >= word_list_offset) {
07805       word_list = line + word_list_offset;
07806    }
07807 
07808    queue_iter = ao2_iterator_init(queues, 0);
07809    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07810       if (!strncasecmp(word, q->name, wordlen) && ++which > state
07811          && (!word_list_offset || !word_in_list(word_list, q->name))) {
07812          ret = ast_strdup(q->name);
07813          queue_t_unref(q, "Done with iterator");
07814          break;
07815       }
07816       queue_t_unref(q, "Done with iterator");
07817    }
07818    ao2_iterator_destroy(&queue_iter);
07819 
07820    /* Pretend "rules" is at the end of the queues list in certain
07821     * circumstances since it is an alternate command that should be
07822     * tab-completable for "queue show" */
07823    if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
07824       ret = ast_strdup("rules");
07825    }
07826 
07827    return ret;
07828 }

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

Definition at line 8256 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

08257 {
08258    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
08259    switch (pos) {
08260    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
08261       return NULL;
08262    case 4: /* only one possible match, "to" */
08263       return state == 0 ? ast_strdup("to") : NULL;
08264    case 5: /* <queue> */
08265       return complete_queue(line, word, pos, state, 0);
08266    case 6: /* only one possible match, "penalty" */
08267       return state == 0 ? ast_strdup("penalty") : NULL;
08268    case 7:
08269       if (state < 100) {      /* 0-99 */
08270          char *num;
08271          if ((num = ast_malloc(3))) {
08272             sprintf(num, "%d", state);
08273          }
08274          return num;
08275       } else {
08276          return NULL;
08277       }
08278    case 8: /* only one possible match, "as" */
08279       return state == 0 ? ast_strdup("as") : NULL;
08280    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
08281       return NULL;
08282    default:
08283       return NULL;
08284    }
08285 }

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

Definition at line 8478 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

08479 {
08480    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
08481    switch (pos) {
08482    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
08483       return NULL;
08484    case 4:  /* only one possible match, "queue" */
08485       return state == 0 ? ast_strdup("queue") : NULL;
08486    case 5:  /* <queue> */
08487       return complete_queue(line, word, pos, state, 0);
08488    case 6: /* "reason" */
08489       return state == 0 ? ast_strdup("reason") : NULL;
08490    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
08491       return NULL;
08492    default:
08493       return NULL;
08494    }
08495 }

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

Definition at line 8386 of file app_queue.c.

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

Referenced by handle_queue_remove_member().

08387 {
08388    int which = 0;
08389    struct call_queue *q;
08390    struct member *m;
08391    struct ao2_iterator queue_iter;
08392    struct ao2_iterator mem_iter;
08393    int wordlen = strlen(word);
08394 
08395    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
08396    if (pos > 5 || pos < 3)
08397       return NULL;
08398    if (pos == 4)   /* only one possible match, 'from' */
08399       return (state == 0 ? ast_strdup("from") : NULL);
08400 
08401    if (pos == 5) {  /* No need to duplicate code */
08402       return complete_queue(line, word, pos, state, 0);
08403    }
08404 
08405    /* here is the case for 3, <member> */
08406    queue_iter = ao2_iterator_init(queues, 0);
08407    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
08408       ao2_lock(q);
08409       mem_iter = ao2_iterator_init(q->members, 0);
08410       while ((m = ao2_iterator_next(&mem_iter))) {
08411          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
08412             char *tmp;
08413             tmp = ast_strdup(m->interface);
08414             ao2_ref(m, -1);
08415             ao2_iterator_destroy(&mem_iter);
08416             ao2_unlock(q);
08417             queue_t_unref(q, "Done with iterator, returning interface name");
08418             ao2_iterator_destroy(&queue_iter);
08419             return tmp;
08420          }
08421          ao2_ref(m, -1);
08422       }
08423       ao2_iterator_destroy(&mem_iter);
08424       ao2_unlock(q);
08425       queue_t_unref(q, "Done with iterator");
08426    }
08427    ao2_iterator_destroy(&queue_iter);
08428 
08429    return NULL;
08430 }

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

Definition at line 8611 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

08612 {
08613    int which = 0;
08614    struct rule_list *rl_iter;
08615    int wordlen = strlen(word);
08616    char *ret = NULL;
08617    if (pos != 3) /* Wha? */ {
08618       return NULL;
08619    }
08620 
08621    AST_LIST_LOCK(&rule_lists);
08622    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08623       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
08624          ret = ast_strdup(rl_iter->name);
08625          break;
08626       }
08627    }
08628    AST_LIST_UNLOCK(&rule_lists);
08629 
08630    return ret;
08631 }

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

Definition at line 8548 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

08549 {
08550    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
08551    switch (pos) {
08552    case 4:
08553       if (state == 0) {
08554          return ast_strdup("on");
08555       } else {
08556          return NULL;
08557       }
08558    case 6:
08559       if (state == 0) {
08560          return ast_strdup("in");
08561       } else {
08562          return NULL;
08563       }
08564    case 7:
08565       return complete_queue(line, word, pos, state, 0);
08566    default:
08567       return NULL;
08568    }
08569 }

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

Definition at line 7830 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07831 {
07832    if (pos == 2) {
07833       return complete_queue(line, word, pos, state, 0);
07834    }
07835    return NULL;
07836 }

static int compress_char ( const char  c  )  [static]

Definition at line 1829 of file app_queue.c.

Referenced by member_hash_fn().

01830 {
01831    if (c < 32)
01832       return 0;
01833    else if (c > 96)
01834       return c - 64;
01835    else
01836       return c - 32;
01837 }

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

Copy rule from global list into specified queue.

Definition at line 6288 of file app_queue.c.

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

Referenced by queue_exec().

06289 {
06290    struct penalty_rule *pr_iter;
06291    struct rule_list *rl_iter;
06292    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
06293    AST_LIST_LOCK(&rule_lists);
06294    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06295       if (!strcasecmp(rl_iter->name, tmp))
06296          break;
06297    }
06298    if (rl_iter) {
06299       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06300          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
06301          if (!new_pr) {
06302             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
06303             break;
06304          }
06305          new_pr->time = pr_iter->time;
06306          new_pr->max_value = pr_iter->max_value;
06307          new_pr->min_value = pr_iter->min_value;
06308          new_pr->max_relative = pr_iter->max_relative;
06309          new_pr->min_relative = pr_iter->min_relative;
06310          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
06311       }
06312    }
06313    AST_LIST_UNLOCK(&rule_lists);
06314 }

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

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

Definition at line 1797 of file app_queue.c.

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

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

01798 {
01799    struct member *cur;
01800    
01801    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01802       cur->penalty = penalty;
01803       cur->paused = paused;
01804       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01805       if (!ast_strlen_zero(state_interface))
01806          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01807       else
01808          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01809       if (!ast_strlen_zero(membername))
01810          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01811       else
01812          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01813       if (!strchr(cur->interface, '/'))
01814          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01815       if (!strncmp(cur->state_interface, "hint:", 5)) {
01816          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01817          char *exten = strsep(&context, "@") + 5;
01818 
01819          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01820          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01821       }
01822       cur->status = get_queue_member_status(cur);
01823    }
01824 
01825    return cur;
01826 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

Definition at line 2400 of file app_queue.c.

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

Referenced by alloc_queue().

02401 {
02402    struct call_queue *q = obj;
02403    int i;
02404 
02405    free_members(q, 1);
02406    ast_string_field_free_memory(q);
02407    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02408       if (q->sound_periodicannounce[i])
02409          free(q->sound_periodicannounce[i]);
02410    }
02411    ao2_ref(q->members, -1);
02412 }

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

Definition at line 1695 of file app_queue.c.

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

Referenced by load_module().

01696 {
01697    enum ast_device_state state;
01698    const char *device;
01699    struct statechange *sc;
01700    size_t datapsize;
01701 
01702    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01703    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01704 
01705    if (ast_strlen_zero(device)) {
01706       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01707       return;
01708    }
01709    datapsize = sizeof(*sc) + strlen(device) + 1;
01710    if (!(sc = ast_calloc(1, datapsize))) {
01711       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01712       return;
01713    }
01714    sc->state = state;
01715    strcpy(sc->dev, device);
01716    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01717       ast_free(sc);
01718    }
01719 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 3194 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

03195 {
03196    o->stillgoing = 0;
03197    ast_hangup(o->chan);
03198    o->chan = NULL;
03199 }

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

direct ouput to manager or cli with proper terminator

Definition at line 7575 of file app_queue.c.

References ast_cli(), and astman_append().

Referenced by __queues_show().

07576 {
07577    if (s)
07578       astman_append(s, "%s\r\n", str);
07579    else
07580       ast_cli(fd, "%s\n", str);
07581 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Dump all members in a specific queue to the database.

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

Definition at line 5659 of file app_queue.c.

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

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

05660 {
05661    struct member *cur_member;
05662    struct ast_str *value;
05663    struct ao2_iterator mem_iter;
05664 
05665    if (!pm_queue) {
05666       return;
05667    }
05668 
05669    /* 4K is a reasonable default for most applications, but we grow to
05670     * accommodate more if necessary. */
05671    if (!(value = ast_str_create(4096))) {
05672       return;
05673    }
05674 
05675    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05676    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05677       if (!cur_member->dynamic) {
05678          ao2_ref(cur_member, -1);
05679          continue;
05680       }
05681 
05682       ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s",
05683          ast_str_strlen(value) ? "|" : "",
05684          cur_member->interface,
05685          cur_member->penalty,
05686          cur_member->paused,
05687          cur_member->membername,
05688          cur_member->state_interface);
05689 
05690       ao2_ref(cur_member, -1);
05691    }
05692    ao2_iterator_destroy(&mem_iter);
05693 
05694    if (ast_str_strlen(value) && !cur_member) {
05695       if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value)))
05696          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05697    } else {
05698       /* Delete the entry if the queue is empty or there is an error */
05699       ast_db_del(pm_family, pm_queue->name);
05700    }
05701 
05702    ast_free(value);
05703 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 4779 of file app_queue.c.

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

Referenced by try_calling().

04780 {
04781    struct queue_end_bridge *qeb = data;
04782    struct call_queue *q = qeb->q;
04783    struct ast_channel *chan = qeb->chan;
04784 
04785    if (ao2_ref(qeb, -1) == 1) {
04786       set_queue_variables(q, chan);
04787       /* This unrefs the reference we made in try_calling when we allocated qeb */
04788       queue_t_unref(q, "Expire bridge_config reference");
04789    }
04790 }

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

Definition at line 4772 of file app_queue.c.

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

Referenced by try_calling().

04773 {
04774    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04775    ao2_ref(qeb, +1);
04776    qeb->chan = originator;
04777 }

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

Definition at line 1753 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_debug, ast_devstate2str(), extensionstate2devicestate(), call_queue::found, call_queue::members, queue_t_unref, queues, member::state_context, member::state_exten, and update_status().

Referenced by load_module(), and unload_module().

01754 {
01755    struct ao2_iterator miter, qiter;
01756    struct member *m;
01757    struct call_queue *q;
01758    int found = 0, device_state = extensionstate2devicestate(state);
01759 
01760    qiter = ao2_iterator_init(queues, 0);
01761    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01762       ao2_lock(q);
01763 
01764       miter = ao2_iterator_init(q->members, 0);
01765       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01766          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01767             update_status(q, m, device_state);
01768             ao2_ref(m, -1);
01769             found = 1;
01770             break;
01771          }
01772       }
01773       ao2_iterator_destroy(&miter);
01774 
01775       ao2_unlock(q);
01776       queue_t_unref(q, "Done with iterator");
01777    }
01778    ao2_iterator_destroy(&qiter);
01779 
01780         if (found) {
01781       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01782    } else {
01783       ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n",
01784            exten, context, device_state, ast_devstate2str(device_state));
01785    }
01786 
01787    return 0;
01788 }

static int extensionstate2devicestate ( int  state  )  [static]

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

Definition at line 1722 of file app_queue.c.

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

Referenced by extension_state_cb(), and get_queue_member_status().

01723 {
01724    switch (state) {
01725    case AST_EXTENSION_NOT_INUSE:
01726       state = AST_DEVICE_NOT_INUSE;
01727       break;
01728    case AST_EXTENSION_INUSE:
01729       state = AST_DEVICE_INUSE;
01730       break;
01731    case AST_EXTENSION_BUSY:
01732       state = AST_DEVICE_BUSY;
01733       break;
01734    case AST_EXTENSION_RINGING:
01735       state = AST_DEVICE_RINGING;
01736       break;
01737    case AST_EXTENSION_ONHOLD:
01738       state = AST_DEVICE_ONHOLD;
01739       break;
01740    case AST_EXTENSION_UNAVAILABLE:
01741       state = AST_DEVICE_UNAVAILABLE;
01742       break;
01743    case AST_EXTENSION_REMOVED:
01744    case AST_EXTENSION_DEACTIVATED:
01745    default:
01746       state = AST_DEVICE_INVALID;
01747       break;
01748    }
01749 
01750    return state;
01751 }

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

find the entry with the best metric, or NULL

Definition at line 3536 of file app_queue.c.

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

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

03537 {
03538    struct callattempt *best = NULL, *cur;
03539 
03540    for (cur = outgoing; cur; cur = cur->q_next) {
03541       if (cur->stillgoing &&              /* Not already done */
03542          !cur->chan &&              /* Isn't already going */
03543          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03544          best = cur;
03545       }
03546    }
03547 
03548    return best;
03549 }

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

Reload a single queue via realtime.

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

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

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

Definition at line 2438 of file app_queue.c.

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

Referenced by load_realtime_queue().

02439 {
02440    struct ast_variable *v;
02441    struct call_queue *q, tmpq = {
02442       .name = queuename,   
02443    };
02444    struct member *m;
02445    struct ao2_iterator mem_iter;
02446    char *interface = NULL;
02447    const char *tmp_name;
02448    char *tmp;
02449    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
02450 
02451    /* Static queues override realtime. */
02452    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02453       ao2_lock(q);
02454       if (!q->realtime) {
02455          if (q->dead) {
02456             ao2_unlock(q);
02457             queue_t_unref(q, "Queue is dead; can't return it");
02458             return NULL;
02459          } else {
02460             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02461             ao2_unlock(q);
02462             return q;
02463          }
02464       }
02465    } else if (!member_config)
02466       /* Not found in the list, and it's not realtime ... */
02467       return NULL;
02468 
02469    /* Check if queue is defined in realtime. */
02470    if (!queue_vars) {
02471       /* Delete queue from in-core list if it has been deleted in realtime. */
02472       if (q) {
02473          /*! \note Hmm, can't seem to distinguish a DB failure from a not
02474             found condition... So we might delete an in-core queue
02475             in case of DB failure. */
02476          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02477 
02478          q->dead = 1;
02479          /* Delete if unused (else will be deleted when last caller leaves). */
02480          queues_t_unlink(queues, q, "Unused; removing from container");
02481          ao2_unlock(q);
02482          queue_t_unref(q, "Queue is dead; can't return it");
02483       }
02484       return NULL;
02485    }
02486 
02487    /* Create a new queue if an in-core entry does not exist yet. */
02488    if (!q) {
02489       struct ast_variable *tmpvar = NULL;
02490       if (!(q = alloc_queue(queuename)))
02491          return NULL;
02492       ao2_lock(q);
02493       clear_queue(q);
02494       q->realtime = 1;
02495       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
02496        * will allocate the members properly
02497        */
02498       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02499          if (!strcasecmp(tmpvar->name, "strategy")) {
02500             q->strategy = strat2int(tmpvar->value);
02501             if (q->strategy < 0) {
02502                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02503                tmpvar->value, q->name);
02504                q->strategy = QUEUE_STRATEGY_RINGALL;
02505             }
02506             break;
02507          }
02508       }
02509       /* We traversed all variables and didn't find a strategy */
02510       if (!tmpvar)
02511          q->strategy = QUEUE_STRATEGY_RINGALL;
02512       queues_t_link(queues, q, "Add queue to container");
02513    }
02514    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
02515 
02516    memset(tmpbuf, 0, sizeof(tmpbuf));
02517    for (v = queue_vars; v; v = v->next) {
02518       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
02519       if (strchr(v->name, '_')) {
02520          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02521          tmp_name = tmpbuf;
02522          tmp = tmpbuf;
02523          while ((tmp = strchr(tmp, '_')))
02524             *tmp++ = '-';
02525       } else
02526          tmp_name = v->name;
02527 
02528       /* NULL values don't get returned from realtime; blank values should
02529        * still get set.  If someone doesn't want a value to be set, they
02530        * should set the realtime column to NULL, not blank. */
02531       queue_set_param(q, tmp_name, v->value, -1, 0);
02532    }
02533 
02534    /* Temporarily set realtime members dead so we can detect deleted ones. */
02535    mem_iter = ao2_iterator_init(q->members, 0);
02536    while ((m = ao2_iterator_next(&mem_iter))) {
02537       if (m->realtime)
02538          m->dead = 1;
02539       ao2_ref(m, -1);
02540    }
02541    ao2_iterator_destroy(&mem_iter);
02542 
02543    while ((interface = ast_category_browse(member_config, interface))) {
02544       rt_handle_member_record(q, interface,
02545          ast_variable_retrieve(member_config, interface, "uniqueid"),
02546          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02547          ast_variable_retrieve(member_config, interface, "penalty"),
02548          ast_variable_retrieve(member_config, interface, "paused"),
02549          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02550    }
02551 
02552    /* Delete all realtime members that have been deleted in DB. */
02553    mem_iter = ao2_iterator_init(q->members, 0);
02554    while ((m = ao2_iterator_next(&mem_iter))) {
02555       if (m->dead) {
02556          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02557          member_remove_from_queue(q, m);
02558       }
02559       ao2_ref(m, -1);
02560    }
02561    ao2_iterator_destroy(&mem_iter);
02562 
02563    ao2_unlock(q);
02564 
02565    return q;
02566 }

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

Iterate through queue's member list and delete them.

Definition at line 2384 of file app_queue.c.

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

Referenced by destroy_queue().

02385 {
02386    /* Free non-dynamic members */
02387    struct member *cur;
02388    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02389 
02390    while ((cur = ao2_iterator_next(&mem_iter))) {
02391       if (all || !cur->dynamic) {
02392          member_remove_from_queue(q, cur);
02393       }
02394       ao2_ref(cur, -1);
02395    }
02396    ao2_iterator_destroy(&mem_iter);
02397 }

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

Definition at line 5936 of file app_queue.c.

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

Referenced by queue_function_memberpenalty_read().

05937 {
05938    int foundqueue = 0, penalty;
05939    struct call_queue *q, tmpq = {
05940       .name = queuename,   
05941    };
05942    struct member *mem;
05943    
05944    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05945       foundqueue = 1;
05946       ao2_lock(q);
05947       if ((mem = interface_exists(q, interface))) {
05948          penalty = mem->penalty;
05949          ao2_ref(mem, -1);
05950          ao2_unlock(q);
05951          queue_t_unref(q, "Search complete");
05952          return penalty;
05953       }
05954       ao2_unlock(q);
05955       queue_t_unref(q, "Search complete");
05956    }
05957 
05958    /* some useful debuging */
05959    if (foundqueue) 
05960       ast_log (LOG_ERROR, "Invalid queuename\n");
05961    else 
05962       ast_log (LOG_ERROR, "Invalid interface\n");
05963 
05964    return RESULT_FAILURE;
05965 }

static int get_member_status ( struct call_queue q,
int  max_penalty,
int  min_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 1541 of file app_queue.c.

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

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

01542 {
01543    struct member *member;
01544    struct ao2_iterator mem_iter;
01545 
01546    ao2_lock(q);
01547    mem_iter = ao2_iterator_init(q->members, 0);
01548    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01549       if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) {
01550          if (conditions & QUEUE_EMPTY_PENALTY) {
01551             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01552             continue;
01553          }
01554       }
01555 
01556       switch (devstate ? ast_device_state(member->state_interface) : member->status) {
01557       case AST_DEVICE_INVALID:
01558          if (conditions & QUEUE_EMPTY_INVALID) {
01559             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01560             break;
01561          }
01562          goto default_case;
01563       case AST_DEVICE_UNAVAILABLE:
01564          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01565             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01566             break;
01567          }
01568          goto default_case;
01569       case AST_DEVICE_INUSE:
01570          if (conditions & QUEUE_EMPTY_INUSE) {
01571             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01572             break;
01573          }
01574          goto default_case;
01575       case AST_DEVICE_RINGING:
01576          if (conditions & QUEUE_EMPTY_RINGING) {
01577             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01578             break;
01579          }
01580          goto default_case;
01581       case AST_DEVICE_UNKNOWN:
01582          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01583             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01584             break;
01585          }
01586          /* Fall-through */
01587       default:
01588       default_case:
01589          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01590             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01591             break;
01592          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01593             ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime);
01594             break;
01595          } else {
01596             ao2_ref(member, -1);
01597             ao2_iterator_destroy(&mem_iter);
01598             ao2_unlock(q);
01599             ast_debug(4, "%s is available.\n", member->membername);
01600             return 0;
01601          }
01602          break;
01603       }
01604    }
01605    ao2_iterator_destroy(&mem_iter);
01606    ao2_unlock(q);
01607 
01608    if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
01609       /* member state still may be RINGING due to lag in event message - check again with device state */
01610       return get_member_status(q, max_penalty, min_penalty, conditions, 1);
01611    }
01612    return -1;
01613 }

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

Definition at line 8312 of file app_queue.c.

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

08313 {
08314    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
08315    int penalty;
08316 
08317    switch ( cmd ) {
08318    case CLI_INIT:
08319       e->command = "queue add member";
08320       e->usage =
08321          "Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
08322          "       Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally:  a penalty, membername and a state_interface\n";
08323       return NULL;
08324    case CLI_GENERATE:
08325       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
08326    }
08327 
08328    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
08329       return CLI_SHOWUSAGE;
08330    } else if (strcmp(a->argv[4], "to")) {
08331       return CLI_SHOWUSAGE;
08332    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
08333       return CLI_SHOWUSAGE;
08334    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
08335       return CLI_SHOWUSAGE;
08336    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
08337       return CLI_SHOWUSAGE;
08338    }
08339 
08340    queuename = a->argv[5];
08341    interface = a->argv[3];
08342    if (a->argc >= 8) {
08343       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
08344          if (penalty < 0) {
08345             ast_cli(a->fd, "Penalty must be >= 0\n");
08346             penalty = 0;
08347          }
08348       } else {
08349          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
08350          penalty = 0;
08351       }
08352    } else {
08353       penalty = 0;
08354    }
08355 
08356    if (a->argc >= 10) {
08357       membername = a->argv[9];
08358    }
08359 
08360    if (a->argc >= 12) {
08361       state_interface = a->argv[11];
08362    }
08363 
08364    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
08365    case RES_OKAY:
08366       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
08367       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
08368       return CLI_SUCCESS;
08369    case RES_EXISTS:
08370       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
08371       return CLI_FAILURE;
08372    case RES_NOSUCHQUEUE:
08373       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
08374       return CLI_FAILURE;
08375    case RES_OUTOFMEMORY:
08376       ast_cli(a->fd, "Out of memory\n");
08377       return CLI_FAILURE;
08378    case RES_NOT_DYNAMIC:
08379       ast_cli(a->fd, "Member not dynamic\n");
08380       return CLI_FAILURE;
08381    default:
08382       return CLI_FAILURE;
08383    }
08384 }

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

Definition at line 8497 of file app_queue.c.

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

08498 {
08499    const char *queuename, *interface, *reason;
08500    int paused;
08501 
08502    switch (cmd) {
08503    case CLI_INIT:
08504       e->command = "queue {pause|unpause} member";
08505       e->usage = 
08506          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
08507          "  Pause or unpause a queue member. Not specifying a particular queue\n"
08508          "  will pause or unpause a member across all queues to which the member\n"
08509          "  belongs.\n";
08510       return NULL;
08511    case CLI_GENERATE:
08512       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
08513    }
08514 
08515    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
08516       return CLI_SHOWUSAGE;
08517    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
08518       return CLI_SHOWUSAGE;
08519    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
08520       return CLI_SHOWUSAGE;
08521    }
08522 
08523 
08524    interface = a->argv[3];
08525    queuename = a->argc >= 6 ? a->argv[5] : NULL;
08526    reason = a->argc == 8 ? a->argv[7] : NULL;
08527    paused = !strcasecmp(a->argv[1], "pause");
08528 
08529    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
08530       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
08531       if (!ast_strlen_zero(queuename))
08532          ast_cli(a->fd, " in queue '%s'", queuename);
08533       if (!ast_strlen_zero(reason))
08534          ast_cli(a->fd, " for reason '%s'", reason);
08535       ast_cli(a->fd, "\n");
08536       return CLI_SUCCESS;
08537    } else {
08538       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
08539       if (!ast_strlen_zero(queuename))
08540          ast_cli(a->fd, " in queue '%s'", queuename);
08541       if (!ast_strlen_zero(reason))
08542          ast_cli(a->fd, " for reason '%s'", reason);
08543       ast_cli(a->fd, "\n");
08544       return CLI_FAILURE;
08545    }
08546 }

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

Definition at line 8706 of file app_queue.c.

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

08707 {
08708    struct ast_flags mask = {0,};
08709    int i;
08710 
08711    switch (cmd) {
08712       case CLI_INIT:
08713          e->command = "queue reload {parameters|members|rules|all}";
08714          e->usage =
08715             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08716             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08717             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08718             "specified in order to know what information to reload. Below is an explanation\n"
08719             "of each of these qualifiers.\n"
08720             "\n"
08721             "\t'members' - reload queue members from queues.conf\n"
08722             "\t'parameters' - reload all queue options except for queue members\n"
08723             "\t'rules' - reload the queuerules.conf file\n"
08724             "\t'all' - reload queue rules, parameters, and members\n"
08725             "\n"
08726             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08727             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08728             "one queue is specified when using this command, reloading queue rules may cause\n"
08729             "other queues to be affected\n";
08730          return NULL;
08731       case CLI_GENERATE:
08732          if (a->pos >= 3) {
08733             /* find the point at which the list of queue names starts */
08734             const char *command_end = a->line + strlen("queue reload ");
08735             command_end = strchr(command_end, ' ');
08736             if (!command_end) {
08737                command_end = a->line + strlen(a->line);
08738             }
08739             return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
08740          } else {
08741             return NULL;
08742          }
08743    }
08744 
08745    if (a->argc < 3)
08746       return CLI_SHOWUSAGE;
08747 
08748    if (!strcasecmp(a->argv[2], "rules")) {
08749       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08750    } else if (!strcasecmp(a->argv[2], "members")) {
08751       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08752    } else if (!strcasecmp(a->argv[2], "parameters")) {
08753       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08754    } else if (!strcasecmp(a->argv[2], "all")) {
08755       ast_set_flag(&mask, AST_FLAGS_ALL);
08756    }
08757 
08758    if (a->argc == 3) {
08759       reload_handler(1, &mask, NULL);
08760       return CLI_SUCCESS;
08761    }
08762 
08763    for (i = 3; i < a->argc; ++i) {
08764       reload_handler(1, &mask, a->argv[i]);
08765    }
08766 
08767    return CLI_SUCCESS;
08768 }

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

Definition at line 8432 of file app_queue.c.

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

08433 {
08434    const char *queuename, *interface;
08435 
08436    switch (cmd) {
08437    case CLI_INIT:
08438       e->command = "queue remove member";
08439       e->usage = 
08440          "Usage: queue remove member <channel> from <queue>\n"
08441          "       Remove a specific channel from a queue.\n";
08442       return NULL;
08443    case CLI_GENERATE:
08444       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
08445    }
08446 
08447    if (a->argc != 6) {
08448       return CLI_SHOWUSAGE;
08449    } else if (strcmp(a->argv[4], "from")) {
08450       return CLI_SHOWUSAGE;
08451    }
08452 
08453    queuename = a->argv[5];
08454    interface = a->argv[3];
08455 
08456    switch (remove_from_queue(queuename, interface)) {
08457    case RES_OKAY:
08458       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
08459       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
08460       return CLI_SUCCESS;
08461    case RES_EXISTS:
08462       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
08463       return CLI_FAILURE;
08464    case RES_NOSUCHQUEUE:
08465       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
08466       return CLI_FAILURE;
08467    case RES_OUTOFMEMORY:
08468       ast_cli(a->fd, "Out of memory\n");
08469       return CLI_FAILURE;
08470    case RES_NOT_DYNAMIC:
08471       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
08472       return CLI_FAILURE;
08473    default:
08474       return CLI_FAILURE;
08475    }
08476 }

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

Definition at line 8667 of file app_queue.c.

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

08668 {
08669    struct ast_flags mask = {QUEUE_RESET_STATS,};
08670    int i;
08671 
08672    switch (cmd) {
08673       case CLI_INIT:
08674          e->command = "queue reset stats";
08675          e->usage =
08676             "Usage: queue reset stats [<queuenames>]\n"
08677             "\n"
08678             "Issuing this command will reset statistics for\n"
08679             "<queuenames>, or for all queues if no queue is\n"
08680             "specified.\n";
08681          return NULL;
08682       case CLI_GENERATE:
08683          if (a->pos >= 3) {
08684             return complete_queue(a->line, a->word, a->pos, a->n, 17);
08685          } else {
08686             return NULL;
08687          }
08688    }
08689 
08690    if (a->argc < 3) {
08691       return CLI_SHOWUSAGE;
08692    }
08693 
08694    if (a->argc == 3) {
08695       reload_handler(1, &mask, NULL);
08696       return CLI_SUCCESS;
08697    }
08698 
08699    for (i = 3; i < a->argc; ++i) {
08700       reload_handler(1, &mask, a->argv[i]);
08701    }
08702 
08703    return CLI_SUCCESS;
08704 }

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

Definition at line 8633 of file app_queue.c.

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

08634 {
08635    const char *rule;
08636    struct rule_list *rl_iter;
08637    struct penalty_rule *pr_iter;
08638    switch (cmd) {
08639    case CLI_INIT:
08640       e->command = "queue show rules";
08641       e->usage =
08642       "Usage: queue show rules [rulename]\n"
08643       "  Show the list of rules associated with rulename. If no\n"
08644       "  rulename is specified, list all rules defined in queuerules.conf\n";
08645       return NULL;
08646    case CLI_GENERATE:
08647       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
08648    }
08649 
08650    if (a->argc != 3 && a->argc != 4)
08651       return CLI_SHOWUSAGE;
08652 
08653    rule = a->argc == 4 ? a->argv[3] : "";
08654    AST_LIST_LOCK(&rule_lists);
08655    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08656       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
08657          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
08658          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
08659             ast_cli(a->fd, "\tAfter %d seconds, adjust QUEUE_MAX_PENALTY %s %d and adjust QUEUE_MIN_PENALTY %s %d\n", pr_iter->time, pr_iter->max_relative ? "by" : "to", pr_iter->max_value, pr_iter->min_relative ? "by" : "to", pr_iter->min_value);
08660          }
08661       }
08662    }
08663    AST_LIST_UNLOCK(&rule_lists);
08664    return CLI_SUCCESS; 
08665 }

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

Definition at line 8571 of file app_queue.c.

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

08572 {
08573    const char *queuename = NULL, *interface;
08574    int penalty = 0;
08575 
08576    switch (cmd) {
08577    case CLI_INIT:
08578       e->command = "queue set penalty";
08579       e->usage = 
08580       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
08581       "  Set a member's penalty in the queue specified. If no queue is specified\n"
08582       "  then that interface's penalty is set in all queues to which that interface is a member\n";
08583       return NULL;
08584    case CLI_GENERATE:
08585       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
08586    }
08587 
08588    if (a->argc != 6 && a->argc != 8) {
08589       return CLI_SHOWUSAGE;
08590    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
08591       return CLI_SHOWUSAGE;
08592    }
08593 
08594    if (a->argc == 8)
08595       queuename = a->argv[7];
08596    interface = a->argv[5];
08597    penalty = atoi(a->argv[3]);
08598 
08599    switch (set_member_penalty(queuename, interface, penalty)) {
08600    case RESULT_SUCCESS:
08601       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08602       return CLI_SUCCESS;
08603    case RESULT_FAILURE:
08604       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08605       return CLI_FAILURE;
08606    default:
08607       return CLI_FAILURE;
08608    }
08609 }

static int handle_statechange ( void *  datap  )  [static]

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

Definition at line 1651 of file app_queue.c.

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

Referenced by device_state_cb().

01652 {
01653    struct statechange *sc = datap;
01654    struct ao2_iterator miter, qiter;
01655    struct member *m;
01656    struct call_queue *q;
01657    char interface[80], *slash_pos;
01658    int found = 0;
01659 
01660    qiter = ao2_iterator_init(queues, 0);
01661    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01662       ao2_lock(q);
01663 
01664       miter = ao2_iterator_init(q->members, 0);
01665       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01666          ast_copy_string(interface, m->state_interface, sizeof(interface));
01667 
01668          if ((slash_pos = strchr(interface, '/')))
01669             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01670                *slash_pos = '\0';
01671 
01672          if (!strcasecmp(interface, sc->dev)) {
01673             found = 1;
01674             update_status(q, m, sc->state);
01675             ao2_ref(m, -1);
01676             break;
01677          }
01678       }
01679       ao2_iterator_destroy(&miter);
01680 
01681       ao2_unlock(q);
01682       queue_t_unref(q, "Done with iterator");
01683    }
01684    ao2_iterator_destroy(&qiter);
01685 
01686    if (found)
01687       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01688    else
01689       ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01690 
01691    ast_free(sc);
01692    return 0;
01693 }

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

Hang up a list of outgoing calls.

Definition at line 3086 of file app_queue.c.

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

Referenced by try_calling().

03087 {
03088    struct callattempt *oo;
03089 
03090    while (outgoing) {
03091       /* If someone else answered the call we should indicate this in the CANCEL */
03092       /* Hangup any existing lines we have open */
03093       if (outgoing->chan && (outgoing->chan != exception)) {
03094          if (exception || cancel_answered_elsewhere)
03095             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03096          ast_hangup(outgoing->chan);
03097       }
03098       oo = outgoing;
03099       outgoing = outgoing->q_next;
03100       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
03101       callattempt_free(oo);
03102    }
03103 }

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

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

Referenced by find_queue_by_name_rt(), and reload_single_queue().

01862 {
01863    int i;
01864    struct penalty_rule *pr_iter;
01865 
01866    q->dead = 0;
01867    q->retry = DEFAULT_RETRY;
01868    q->timeout = DEFAULT_TIMEOUT;
01869    q->maxlen = 0;
01870    q->ringlimit = 0;
01871    q->announcefrequency = 0;
01872    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01873    q->announceholdtime = 1;
01874    q->announcepositionlimit = 10; /* Default 10 positions */
01875    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01876    q->roundingseconds = 0; /* Default - don't announce seconds */
01877    q->servicelevel = 0;
01878    q->ringinuse = 1;
01879    q->announce_to_first_user = 0;
01880    q->setinterfacevar = 0;
01881    q->setqueuevar = 0;
01882    q->setqueueentryvar = 0;
01883    q->autofill = autofill_default;
01884    q->montype = montype_default;
01885    q->monfmt[0] = '\0';
01886    q->reportholdtime = 0;
01887    q->wrapuptime = 0;
01888    q->penaltymemberslimit = 0;
01889    q->joinempty = 0;
01890    q->leavewhenempty = 0;
01891    q->memberdelay = 0;
01892    q->maskmemberstatus = 0;
01893    q->eventwhencalled = 0;
01894    q->weight = 0;
01895    q->timeoutrestart = 0;
01896    q->periodicannouncefrequency = 0;
01897    q->randomperiodicannounce = 0;
01898    q->numperiodicannounce = 0;
01899    q->autopause = QUEUE_AUTOPAUSE_OFF;
01900    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01901    if (!q->members) {
01902       if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
01903          /* linear strategy depends on order, so we have to place all members in a single bucket */
01904          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01905       else
01906          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01907    }
01908    q->found = 1;
01909 
01910    ast_string_field_set(q, sound_next, "queue-youarenext");
01911    ast_string_field_set(q, sound_thereare, "queue-thereare");
01912    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01913    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01914    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01915    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01916    ast_string_field_set(q, sound_minutes, "queue-minutes");
01917    ast_string_field_set(q, sound_minute, "queue-minute");
01918    ast_string_field_set(q, sound_seconds, "queue-seconds");
01919    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01920    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01921 
01922    if (!q->sound_periodicannounce[0]) {
01923       q->sound_periodicannounce[0] = ast_str_create(32);
01924    }
01925 
01926    if (q->sound_periodicannounce[0]) {
01927       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01928    }
01929 
01930    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01931       if (q->sound_periodicannounce[i])
01932          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01933    }
01934 
01935    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01936       ast_free(pr_iter);
01937 }

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

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

Definition at line 1511 of file app_queue.c.

References call_queue::head, and queue_ref().

Referenced by join_queue().

01512 {
01513    struct queue_ent *cur;
01514 
01515    if (!q || !new)
01516       return;
01517    if (prev) {
01518       cur = prev->next;
01519       prev->next = new;
01520    } else {
01521       cur = q->head;
01522       q->head = new;
01523    }
01524    new->next = cur;
01525 
01526    /* every queue_ent must have a reference to it's parent call_queue, this
01527     * reference does not go away until the end of the queue_ent's life, meaning
01528     * that even when the queue_ent leaves the call_queue this ref must remain. */
01529    queue_ref(q);
01530    new->parent = q;
01531    new->pos = ++(*pos);
01532    new->opos = *pos;
01533 }

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

Change queue penalty by adding rule.

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

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

Definition at line 1968 of file app_queue.c.

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

Referenced by reload_queue_rules().

01969 {
01970    char *timestr, *maxstr, *minstr, *contentdup;
01971    struct penalty_rule *rule = NULL, *rule_iter;
01972    struct rule_list *rl_iter;
01973    int penaltychangetime, inserted = 0;
01974 
01975    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01976       return -1;
01977    }
01978 
01979    contentdup = ast_strdupa(content);
01980    
01981    if (!(maxstr = strchr(contentdup, ','))) {
01982       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01983       ast_free(rule);
01984       return -1;
01985    }
01986 
01987    *maxstr++ = '\0';
01988    timestr = contentdup;
01989 
01990    if ((penaltychangetime = atoi(timestr)) < 0) {
01991       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01992       ast_free(rule);
01993       return -1;
01994    }
01995 
01996    rule->time = penaltychangetime;
01997 
01998    if ((minstr = strchr(maxstr,',')))
01999       *minstr++ = '\0';
02000    
02001    /* The last check will evaluate true if either no penalty change is indicated for a given rule
02002     * OR if a min penalty change is indicated but no max penalty change is */
02003    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
02004       rule->max_relative = 1;
02005    }
02006 
02007    rule->max_value = atoi(maxstr);
02008 
02009    if (!ast_strlen_zero(minstr)) {
02010       if (*minstr == '+' || *minstr == '-')
02011          rule->min_relative = 1;
02012       rule->min_value = atoi(minstr);
02013    } else /*there was no minimum specified, so assume this means no change*/
02014       rule->min_relative = 1;
02015 
02016    /*We have the rule made, now we need to insert it where it belongs*/
02017    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
02018       if (strcasecmp(rl_iter->name, list_name))
02019          continue;
02020 
02021       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
02022          if (rule->time < rule_iter->time) {
02023             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
02024             inserted = 1;
02025             break;
02026          }
02027       }
02028       AST_LIST_TRAVERSE_SAFE_END;
02029    
02030       if (!inserted) {
02031          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
02032          inserted = 1;
02033       }
02034 
02035       break;
02036    }
02037 
02038    if (!inserted) {
02039       ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
02040       ast_free(rule);
02041       return -1;
02042    }
02043    return 0;
02044 }

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

Definition at line 1349 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

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

01350 {
01351    int x;
01352 
01353    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01354       if (strategy == strategies[x].strategy)
01355          return strategies[x].name;
01356    }
01357 
01358    return "<unknown>";
01359 }

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

Definition at line 5633 of file app_queue.c.

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

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

05634 {
05635    struct member *mem;
05636    struct ao2_iterator mem_iter;
05637 
05638    if (!q)
05639       return NULL;
05640 
05641    mem_iter = ao2_iterator_init(q->members, 0);
05642    while ((mem = ao2_iterator_next(&mem_iter))) {
05643       if (!strcasecmp(interface, mem->interface)) {
05644          ao2_iterator_destroy(&mem_iter);
05645          return mem;
05646       }
05647       ao2_ref(mem, -1);
05648    }
05649    ao2_iterator_destroy(&mem_iter);
05650 
05651    return NULL;
05652 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

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

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

Definition at line 4320 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

04321 {
04322    struct queue_ent *ch;
04323    int res;
04324    int avl;
04325    int idx = 0;
04326    /* This needs a lock. How many members are available to be served? */
04327    ao2_lock(qe->parent);
04328 
04329    avl = num_available_members(qe->parent);
04330 
04331    ch = qe->parent->head;
04332 
04333    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
04334 
04335    while ((idx < avl) && (ch) && (ch != qe)) {
04336       if (!ch->pending)
04337          idx++;
04338       ch = ch->next;       
04339    }
04340 
04341    ao2_unlock(qe->parent);
04342    /* If the queue entry is within avl [the number of available members] calls from the top ... 
04343     * Autofill and position check added to support autofill=no (as only calls
04344     * from the front of the queue are valid when autofill is disabled)
04345     */
04346    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
04347       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
04348       res = 1;
04349    } else {
04350       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
04351       res = 0;
04352    }
04353 
04354    return res;
04355 }

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

Definition at line 2696 of file app_queue.c.

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

Referenced by queue_exec().

02697 {
02698    struct call_queue *q;
02699    struct queue_ent *cur, *prev = NULL;
02700    int res = -1;
02701    int pos = 0;
02702    int inserted = 0;
02703 
02704    if (!(q = load_realtime_queue(queuename)))
02705       return res;
02706 
02707    ao2_lock(q);
02708 
02709    /* This is our one */
02710    if (q->joinempty) {
02711       int status = 0;
02712       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty, 0))) {
02713          *reason = QUEUE_JOINEMPTY;
02714          ao2_unlock(q);
02715          queue_t_unref(q, "Done with realtime queue");
02716          return res;
02717       }
02718    }
02719    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02720       *reason = QUEUE_FULL;
02721    else if (*reason == QUEUE_UNKNOWN) {
02722       /* There's space for us, put us at the right position inside
02723        * the queue.
02724        * Take into account the priority of the calling user */
02725       inserted = 0;
02726       prev = NULL;
02727       cur = q->head;
02728       while (cur) {
02729          /* We have higher priority than the current user, enter
02730           * before him, after all the other users with priority
02731           * higher or equal to our priority. */
02732          if ((!inserted) && (qe->prio > cur->prio)) {
02733             insert_entry(q, prev, qe, &pos);
02734             inserted = 1;
02735          }
02736          /* <= is necessary for the position comparison because it may not be possible to enter
02737           * at our desired position since higher-priority callers may have taken the position we want
02738           */
02739          if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02740             insert_entry(q, prev, qe, &pos);
02741             inserted = 1;
02742             /*pos is incremented inside insert_entry, so don't need to add 1 here*/
02743             if (position < pos) {
02744                ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02745             }
02746          }
02747          cur->pos = ++pos;
02748          prev = cur;
02749          cur = cur->next;
02750       }
02751       /* No luck, join at the end of the queue */
02752       if (!inserted)
02753          insert_entry(q, prev, qe, &pos);
02754       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02755       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02756       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02757       q->count++;
02758       res = 0;
02759       ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02760          "Channel: %s\r\n"
02761          "CallerIDNum: %s\r\n"
02762          "CallerIDName: %s\r\n"
02763          "ConnectedLineNum: %s\r\n"
02764          "ConnectedLineName: %s\r\n"
02765          "Queue: %s\r\n"
02766          "Position: %d\r\n"
02767          "Count: %d\r\n"
02768          "Uniqueid: %s\r\n",
02769          qe->chan->name,
02770          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02771          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02772          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02773          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
02774          q->name, qe->pos, q->count, qe->chan->uniqueid );
02775       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02776    }
02777    ao2_unlock(q);
02778    queue_t_unref(q, "Done with realtime queue");
02779 
02780    return res;
02781 }

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

Definition at line 7296 of file app_queue.c.

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

Referenced by reload_single_queue().

07297 {
07298    struct member *member = obj;
07299 
07300    if (!member->delme) {
07301       member->status = get_queue_member_status(member);
07302       return 0;
07303    } else {
07304       return CMP_MATCH;
07305    }
07306 }

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

Definition at line 7440 of file app_queue.c.

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

Referenced by reload_queues().

07441 {
07442    struct call_queue *q = obj;
07443    char *queuename = arg;
07444    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
07445       return CMP_MATCH;
07446    } else {
07447       return 0;
07448    }
07449 }

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

References ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_manager_event, ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, queue_ent::parent, pbx_builtin_setvar_helper(), queue_ent::pos, queue_t_ref, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, SENTINEL, and var.

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

03009 {
03010    struct call_queue *q;
03011    struct queue_ent *current, *prev = NULL;
03012    struct penalty_rule *pr_iter;
03013    int pos = 0;
03014 
03015    if (!(q = qe->parent))
03016       return;
03017    queue_t_ref(q, "Copy queue pointer from queue entry");
03018    ao2_lock(q);
03019 
03020    prev = NULL;
03021    for (current = q->head; current; current = current->next) {
03022       if (current == qe) {
03023          char posstr[20];
03024          q->count--;
03025 
03026          /* Take us out of the queue */
03027          ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
03028             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
03029             qe->chan->name, q->name,  q->count, qe->pos, qe->chan->uniqueid);
03030          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
03031          /* Take us out of the queue */
03032          if (prev)
03033             prev->next = current->next;
03034          else
03035             q->head = current->next;
03036          /* Free penalty rules */
03037          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
03038             ast_free(pr_iter);
03039          snprintf(posstr, sizeof(posstr), "%d", qe->pos);
03040          pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
03041       } else {
03042          /* Renumber the people after us in the queue based on a new count */
03043          current->pos = ++pos;
03044          prev = current;
03045       }
03046    }
03047    ao2_unlock(q);
03048 
03049    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
03050    if (q->realtime) {
03051       struct ast_variable *var;
03052       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
03053          q->dead = 1;
03054       } else {
03055          ast_variables_destroy(var);
03056       }
03057    }
03058 
03059    if (q->dead) { 
03060       /* It's dead and nobody is in it, so kill it */
03061       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
03062    }
03063    /* unref the explicit ref earlier in the function */
03064    queue_t_unref(q, "Expire copied reference");
03065 }

static int load_module ( void   )  [static]

Definition at line 9120 of file app_queue.c.

References ao2_container_alloc, aqm_exec(), ARRAY_LEN, ast_add_extension2(), ast_cli_register_multiple(), ast_context_find_or_create(), ast_custom_function_register, ast_data_register_multiple, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), ast_extension_state_add(), AST_FLAGS_ALL, ast_free_ptr(), ast_log(), ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application_xml, ast_strdup, ast_taskprocessor_get(), device_state_cb(), EVENT_FLAG_AGENT, extension_state_cb(), LOG_ERROR, LOG_WARNING, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_log_custom(), manager_queue_member_penalty(), manager_queue_reload(), manager_queue_reset(), manager_queue_rule_show(), manager_queues_show(), manager_queues_status(), manager_queues_summary(), manager_remove_queue_member(), MAX_QUEUE_BUCKETS, pqm_exec(), ql_exec(), queue_cmp_cb(), queue_exec(), queue_hash_cb(), queues, reload_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().

09121 {
09122    int res;
09123    struct ast_context *con;
09124    struct ast_flags mask = {AST_FLAGS_ALL, };
09125 
09126    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
09127 
09128    use_weight = 0;
09129 
09130    if (reload_handler(0, &mask, NULL))
09131       return AST_MODULE_LOAD_DECLINE;
09132 
09133    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
09134    if (!con)
09135       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
09136    else
09137       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
09138 
09139    if (queue_persistent_members)
09140       reload_queue_members();
09141 
09142    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
09143 
09144    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
09145    res = ast_register_application_xml(app, queue_exec);
09146    res |= ast_register_application_xml(app_aqm, aqm_exec);
09147    res |= ast_register_application_xml(app_rqm, rqm_exec);
09148    res |= ast_register_application_xml(app_pqm, pqm_exec);
09149    res |= ast_register_application_xml(app_upqm, upqm_exec);
09150    res |= ast_register_application_xml(app_ql, ql_exec);
09151    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
09152    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
09153    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
09154    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
09155    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
09156    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
09157    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
09158    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
09159    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
09160    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
09161    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
09162    res |= ast_custom_function_register(&queuevar_function);
09163    res |= ast_custom_function_register(&queueexists_function);
09164    res |= ast_custom_function_register(&queuemembercount_function);
09165    res |= ast_custom_function_register(&queuemembercount_dep);
09166    res |= ast_custom_function_register(&queuememberlist_function);
09167    res |= ast_custom_function_register(&queuewaitingcount_function);
09168    res |= ast_custom_function_register(&queuememberpenalty_function);
09169    res |= ast_custom_function_register(&queuememberstatus_function);
09170    res |= ast_custom_function_register(&queuememberpaused_function);
09171 
09172    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
09173       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
09174    }
09175 
09176    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
09177    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
09178       res = -1;
09179    }
09180 
09181    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
09182 
09183    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
09184 
09185    return res ? AST_MODULE_LOAD_DECLINE : 0;
09186 }

static struct call_queue* load_realtime_queue ( const char *  queuename  )  [static, read]
Note:
Returns a reference to the loaded realtime queue.

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

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

Definition at line 2569 of file app_queue.c.

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

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

02570 {
02571    struct ast_variable *queue_vars;
02572    struct ast_config *member_config = NULL;
02573    struct call_queue *q = NULL, tmpq = {
02574       .name = queuename,   
02575    };
02576    int prev_weight = 0;
02577 
02578    /* Find the queue in the in-core list first. */
02579    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02580 
02581    if (!q || q->realtime) {
02582       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
02583          queue operations while waiting for the DB.
02584 
02585          This will be two separate database transactions, so we might
02586          see queue parameters as they were before another process
02587          changed the queue and member list as it was after the change.
02588          Thus we might see an empty member list when a queue is
02589          deleted. In practise, this is unlikely to cause a problem. */
02590 
02591       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02592       if (queue_vars) {
02593          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02594          if (!member_config) {
02595             ast_debug(1, "No queue_members defined in config extconfig.conf\n");
02596             member_config = ast_config_new();
02597          }
02598       }
02599       if (q) {
02600          prev_weight = q->weight ? 1 : 0;
02601          queue_t_unref(q, "Need to find realtime queue");
02602       }
02603 
02604       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02605       ast_config_destroy(member_config);
02606       ast_variables_destroy(queue_vars);
02607 
02608       /* update the use_weight value if the queue's has gained or lost a weight */
02609       if (q) {
02610          if (!q->weight && prev_weight) {
02611             ast_atomic_fetchadd_int(&use_weight, -1);
02612          }
02613          if (q->weight && !prev_weight) {
02614             ast_atomic_fetchadd_int(&use_weight, +1);
02615          }
02616       }
02617       /* Other cases will end up with the proper value for use_weight */
02618    } else {
02619       update_realtime_members(q);
02620    }
02621    return q;
02622 }

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

Definition at line 8079 of file app_queue.c.

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

Referenced by load_module().

08080 {
08081    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
08082    int paused, penalty = 0;
08083 
08084    queuename = astman_get_header(m, "Queue");
08085    interface = astman_get_header(m, "Interface");
08086    penalty_s = astman_get_header(m, "Penalty");
08087    paused_s = astman_get_header(m, "Paused");
08088    membername = astman_get_header(m, "MemberName");
08089    state_interface = astman_get_header(m, "StateInterface");
08090 
08091    if (ast_strlen_zero(queuename)) {
08092       astman_send_error(s, m, "'Queue' not specified.");
08093       return 0;
08094    }
08095 
08096    if (ast_strlen_zero(interface)) {
08097       astman_send_error(s, m, "'Interface' not specified.");
08098       return 0;
08099    }
08100 
08101    if (ast_strlen_zero(penalty_s))
08102       penalty = 0;
08103    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
08104       penalty = 0;
08105 
08106    if (ast_strlen_zero(paused_s))
08107       paused = 0;
08108    else
08109       paused = abs(ast_true(paused_s));
08110 
08111    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
08112    case RES_OKAY:
08113       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
08114       astman_send_ack(s, m, "Added interface to queue");
08115       break;
08116    case RES_EXISTS:
08117       astman_send_error(s, m, "Unable to add interface: Already there");
08118       break;
08119    case RES_NOSUCHQUEUE:
08120       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
08121       break;
08122    case RES_OUTOFMEMORY:
08123       astman_send_error(s, m, "Out of memory");
08124       break;
08125    }
08126 
08127    return 0;
08128 }

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

Definition at line 8164 of file app_queue.c.

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

Referenced by load_module().

08165 {
08166    const char *queuename, *interface, *paused_s, *reason;
08167    int paused;
08168 
08169    interface = astman_get_header(m, "Interface");
08170    paused_s = astman_get_header(m, "Paused");
08171    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
08172    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
08173 
08174    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
08175       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
08176       return 0;
08177    }
08178 
08179    paused = abs(ast_true(paused_s));
08180 
08181    if (set_member_paused(queuename, interface, reason, paused))
08182       astman_send_error(s, m, "Interface not found");
08183    else
08184       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
08185    return 0;
08186 }

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

Definition at line 8188 of file app_queue.c.

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

Referenced by load_module().

08189 {
08190    const char *queuename, *event, *message, *interface, *uniqueid;
08191 
08192    queuename = astman_get_header(m, "Queue");
08193    uniqueid = astman_get_header(m, "UniqueId");
08194    interface = astman_get_header(m, "Interface");
08195    event = astman_get_header(m, "Event");
08196    message = astman_get_header(m, "Message");
08197 
08198    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
08199       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
08200       return 0;
08201    }
08202 
08203    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
08204    astman_send_ack(s, m, "Event added successfully");
08205 
08206    return 0;
08207 }

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

Definition at line 8287 of file app_queue.c.

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

Referenced by load_module().

08288 {
08289    const char *queuename, *interface, *penalty_s;
08290    int penalty;
08291 
08292    interface = astman_get_header(m, "Interface");
08293    penalty_s = astman_get_header(m, "Penalty");
08294    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
08295    queuename = astman_get_header(m, "Queue");
08296 
08297    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
08298       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
08299       return 0;
08300    }
08301  
08302    penalty = atoi(penalty_s);
08303 
08304    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
08305       astman_send_error(s, m, "Invalid interface, queuename or penalty");
08306    else
08307       astman_send_ack(s, m, "Interface penalty set successfully");
08308 
08309    return 0;
08310 }

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

Definition at line 8209 of file app_queue.c.

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

Referenced by load_module().

08210 {
08211    struct ast_flags mask = {0,};
08212    const char *queuename = NULL;
08213    int header_found = 0;
08214 
08215    queuename = astman_get_header(m, "Queue");
08216    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
08217       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08218       header_found = 1;
08219    }
08220    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
08221       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08222       header_found = 1;
08223    }
08224    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
08225       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08226       header_found = 1;
08227    }
08228 
08229    if (!header_found) {
08230       ast_set_flag(&mask, AST_FLAGS_ALL);
08231    }
08232 
08233    if (!reload_handler(1, &mask, queuename)) {
08234       astman_send_ack(s, m, "Queue reloaded successfully");
08235    } else {
08236       astman_send_error(s, m, "Error encountered while reloading queue");
08237    }
08238    return 0;
08239 }

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

Definition at line 8241 of file app_queue.c.

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

Referenced by load_module().

08242 {
08243    const char *queuename = NULL;
08244    struct ast_flags mask = {QUEUE_RESET_STATS,};
08245    
08246    queuename = astman_get_header(m, "Queue");
08247 
08248    if (!reload_handler(1, &mask, queuename)) {
08249       astman_send_ack(s, m, "Queue stats reset successfully");
08250    } else {
08251       astman_send_error(s, m, "Error encountered while resetting queue stats");
08252    }
08253    return 0;
08254 }

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

Definition at line 7867 of file app_queue.c.

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

Referenced by load_module().

07868 {
07869    const char *rule = astman_get_header(m, "Rule");
07870    const char *id = astman_get_header(m, "ActionID");
07871    struct rule_list *rl_iter;
07872    struct penalty_rule *pr_iter;
07873 
07874    astman_append(s, "Response: Success\r\n");
07875    if (!ast_strlen_zero(id)) {
07876       astman_append(s, "ActionID: %s\r\n", id);
07877    }
07878 
07879    AST_LIST_LOCK(&rule_lists);
07880    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07881       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07882          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07883          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07884             astman_append(s, "Rule: %d,%s%d,%s%d\r\n", pr_iter->time, pr_iter->max_relative && pr_iter->max_value >= 0 ? "+" : "", pr_iter->max_value, pr_iter->min_relative && pr_iter->min_value >= 0 ? "+" : "", pr_iter->min_value );
07885          }
07886          if (!ast_strlen_zero(rule))
07887             break;
07888       }
07889    }
07890    AST_LIST_UNLOCK(&rule_lists);
07891 
07892    /*
07893     * Two blank lines instead of one because the Response and
07894     * ActionID headers used to not be present.
07895     */
07896    astman_append(s, "\r\n\r\n");
07897 
07898    return RESULT_SUCCESS;
07899 }

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

Definition at line 7857 of file app_queue.c.

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

Referenced by load_module().

07858 {
07859    static const char * const a[] = { "queue", "show" };
07860 
07861    __queues_show(s, -1, 2, a);
07862    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07863 
07864    return RESULT_SUCCESS;
07865 }

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

Queue status info via AMI.

Definition at line 7977 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), ast_channel::caller, member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::connected, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, ast_party_connected_line::id, ast_party_caller::id, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, ast_party_id::name, ast_party_id::number, member::paused, member::penalty, queue_ent::pos, queue_t_unref, queues, RESULT_SUCCESS, call_queue::ringlimit, S_COR, call_queue::servicelevel, queue_ent::start, member::status, ast_party_name::str, ast_party_number::str, call_queue::strategy, call_queue::talktime, ast_party_name::valid, ast_party_number::valid, and call_queue::weight.

Referenced by load_module().

07978 {
07979    time_t now;
07980    int pos;
07981    const char *id = astman_get_header(m,"ActionID");
07982    const char *queuefilter = astman_get_header(m,"Queue");
07983    const char *memberfilter = astman_get_header(m,"Member");
07984    char idText[256] = "";
07985    struct call_queue *q;
07986    struct queue_ent *qe;
07987    float sl = 0;
07988    struct member *mem;
07989    struct ao2_iterator queue_iter;
07990    struct ao2_iterator mem_iter;
07991 
07992    astman_send_ack(s, m, "Queue status will follow");
07993    time(&now);
07994    if (!ast_strlen_zero(id))
07995       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07996 
07997    queue_iter = ao2_iterator_init(queues, 0);
07998    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07999       ao2_lock(q);
08000 
08001       /* List queue properties */
08002       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
08003          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
08004          astman_append(s, "Event: QueueParams\r\n"
08005             "Queue: %s\r\n"
08006             "Max: %d\r\n"
08007             "Strategy: %s\r\n"
08008             "Calls: %d\r\n"
08009             "Holdtime: %d\r\n"
08010             "TalkTime: %d\r\n"
08011             "Completed: %d\r\n"
08012             "Abandoned: %d\r\n"
08013             "ServiceLevel: %d\r\n"
08014             "ServicelevelPerf: %2.1f\r\n"
08015             "RingLimit: %d\r\n"
08016             "Weight: %d\r\n"
08017             "%s"
08018             "\r\n",
08019             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
08020             q->callsabandoned, q->servicelevel, sl, q->ringlimit, q->weight, idText);
08021          /* List Queue Members */
08022          mem_iter = ao2_iterator_init(q->members, 0);
08023          while ((mem = ao2_iterator_next(&mem_iter))) {
08024             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
08025                astman_append(s, "Event: QueueMember\r\n"
08026                   "Queue: %s\r\n"
08027                   "Name: %s\r\n"
08028                   "Location: %s\r\n"
08029                   "Membership: %s\r\n"
08030                   "Penalty: %d\r\n"
08031                   "CallsTaken: %d\r\n"
08032                   "LastCall: %d\r\n"
08033                   "Status: %d\r\n"
08034                   "Paused: %d\r\n"
08035                   "%s"
08036                   "\r\n",
08037                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
08038                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
08039             }
08040             ao2_ref(mem, -1);
08041          }
08042          ao2_iterator_destroy(&mem_iter);
08043          /* List Queue Entries */
08044          pos = 1;
08045          for (qe = q->head; qe; qe = qe->next) {
08046             astman_append(s, "Event: QueueEntry\r\n"
08047                "Queue: %s\r\n"
08048                "Position: %d\r\n"
08049                "Channel: %s\r\n"
08050                "Uniqueid: %s\r\n"
08051                "CallerIDNum: %s\r\n"
08052                "CallerIDName: %s\r\n"
08053                "ConnectedLineNum: %s\r\n"
08054                "ConnectedLineName: %s\r\n"
08055                "Wait: %ld\r\n"
08056                "%s"
08057                "\r\n",
08058                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
08059                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
08060                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
08061                S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
08062                S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
08063                (long) (now - qe->start), idText);
08064          }
08065       }
08066       ao2_unlock(q);
08067       queue_t_unref(q, "Done with iterator");
08068    }
08069    ao2_iterator_destroy(&queue_iter);
08070 
08071    astman_append(s,
08072       "Event: QueueStatusComplete\r\n"
08073       "%s"
08074       "\r\n",idText);
08075 
08076    return RESULT_SUCCESS;
08077 }

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

Summary of queue info via the AMI.

Definition at line 7902 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), call_queue::head, call_queue::holdtime, member_status_available(), call_queue::members, member::paused, queue_t_unref, queues, RESULT_SUCCESS, queue_ent::start, member::status, and call_queue::talktime.

Referenced by load_module().

07903 {
07904    time_t now;
07905    int qmemcount = 0;
07906    int qmemavail = 0;
07907    int qchancount = 0;
07908    int qlongestholdtime = 0;
07909    const char *id = astman_get_header(m, "ActionID");
07910    const char *queuefilter = astman_get_header(m, "Queue");
07911    char idText[256] = "";
07912    struct call_queue *q;
07913    struct queue_ent *qe;
07914    struct member *mem;
07915    struct ao2_iterator queue_iter;
07916    struct ao2_iterator mem_iter;
07917 
07918    astman_send_ack(s, m, "Queue summary will follow");
07919    time(&now);
07920    if (!ast_strlen_zero(id))
07921       snprintf(idText, 256, "ActionID: %s\r\n", id);
07922    queue_iter = ao2_iterator_init(queues, 0);
07923    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07924       ao2_lock(q);
07925 
07926       /* List queue properties */
07927       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07928          /* Reset the necessary local variables if no queuefilter is set*/
07929          qmemcount = 0;
07930          qmemavail = 0;
07931          qchancount = 0;
07932          qlongestholdtime = 0;
07933 
07934          /* List Queue Members */
07935          mem_iter = ao2_iterator_init(q->members, 0);
07936          while ((mem = ao2_iterator_next(&mem_iter))) {
07937             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07938                ++qmemcount;
07939                if (member_status_available(mem->status) && !mem->paused) {
07940                   ++qmemavail;
07941                }
07942             }
07943             ao2_ref(mem, -1);
07944          }
07945          ao2_iterator_destroy(&mem_iter);
07946          for (qe = q->head; qe; qe = qe->next) {
07947             if ((now - qe->start) > qlongestholdtime) {
07948                qlongestholdtime = now - qe->start;
07949             }
07950             ++qchancount;
07951          }
07952          astman_append(s, "Event: QueueSummary\r\n"
07953             "Queue: %s\r\n"
07954             "LoggedIn: %d\r\n"
07955             "Available: %d\r\n"
07956             "Callers: %d\r\n" 
07957             "HoldTime: %d\r\n"
07958             "TalkTime: %d\r\n"
07959             "LongestHoldTime: %d\r\n"
07960             "%s"
07961             "\r\n",
07962             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07963       }
07964       ao2_unlock(q);
07965       queue_t_unref(q, "Done with iterator");
07966    }
07967    ao2_iterator_destroy(&queue_iter);
07968    astman_append(s,
07969       "Event: QueueSummaryComplete\r\n"
07970       "%s"
07971       "\r\n", idText);
07972 
07973    return RESULT_SUCCESS;
07974 }

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

Definition at line 8130 of file app_queue.c.

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

Referenced by load_module().

08131 {
08132    const char *queuename, *interface;
08133 
08134    queuename = astman_get_header(m, "Queue");
08135    interface = astman_get_header(m, "Interface");
08136 
08137    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
08138       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
08139       return 0;
08140    }
08141 
08142    switch (remove_from_queue(queuename, interface)) {
08143    case RES_OKAY:
08144       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
08145       astman_send_ack(s, m, "Removed interface from queue");
08146       break;
08147    case RES_EXISTS:
08148       astman_send_error(s, m, "Unable to remove interface: Not there");
08149       break;
08150    case RES_NOSUCHQUEUE:
08151       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
08152       break;
08153    case RES_OUTOFMEMORY:
08154       astman_send_error(s, m, "Out of memory");
08155       break;
08156    case RES_NOT_DYNAMIC:
08157       astman_send_error(s, m, "Member not dynamic");
08158       break;
08159    }
08160 
08161    return 0;
08162 }

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

Definition at line 7429 of file app_queue.c.

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

Referenced by reload_queues().

07430 {
07431    struct call_queue *q = obj;
07432    char *queuename = arg;
07433    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07434       q->dead = 1;
07435       q->found = 0;
07436    }
07437    return 0;
07438 }

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

Definition at line 7287 of file app_queue.c.

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

Referenced by reload_single_queue().

07288 {
07289    struct member *member = obj;
07290    if (!member->dynamic && !member->realtime) {
07291       member->delme = 1;
07292    }
07293    return 0;
07294 }

static void member_add_to_queue ( struct call_queue queue,
struct member mem 
) [static]

Definition at line 2296 of file app_queue.c.

References ao2_container_count(), ao2_link, ao2_lock, ao2_unlock, call_queue::members, and member::queuepos.

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

02297 {
02298    ao2_lock(queue->members);
02299    mem->queuepos = ao2_container_count(queue->members);
02300    ao2_link(queue->members, mem);
02301    ao2_unlock(queue->members);
02302 }

static void member_call_pending_clear ( struct member mem  )  [static]

Definition at line 3260 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry(), and ring_entry().

03261 {
03262    ao2_lock(mem);
03263    mem->call_pending = 0;
03264    ao2_unlock(mem);
03265 }

static int member_call_pending_set ( struct member mem  )  [static]

Definition at line 3275 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry().

03276 {
03277    int old_pending;
03278 
03279    ao2_lock(mem);
03280    old_pending = mem->call_pending;
03281    mem->call_pending = 1;
03282    ao2_unlock(mem);
03283 
03284    return old_pending;
03285 }

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

Definition at line 1851 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01852 {
01853    struct member *mem1 = obj1, *mem2 = obj2;
01854    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01855 }

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

Definition at line 1839 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01840 {
01841    const struct member *mem = obj;
01842    const char *chname = strchr(mem->interface, '/');
01843    int ret = 0, i;
01844    if (!chname)
01845       chname = mem->interface;
01846    for (i = 0; i < 5 && chname[i]; i++)
01847       ret += compress_char(chname[i]) << (i * 6);
01848    return ret;
01849 }

static void member_remove_from_queue ( struct call_queue queue,
struct member mem 
) [static]

Definition at line 2310 of file app_queue.c.

References ao2_lock, ao2_unlink, ao2_unlock, call_queue::members, and queue_member_follower_removal().

Referenced by find_queue_by_name_rt(), free_members(), remove_from_queue(), and update_realtime_members().

02311 {
02312    ao2_lock(queue->members);
02313    queue_member_follower_removal(queue, mem);
02314    ao2_unlink(queue->members, mem);
02315    ao2_unlock(queue->members);
02316 }

static int member_status_available ( int  status  )  [static]

Definition at line 3247 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.

Referenced by can_ring_entry(), and manager_queues_summary().

03248 {
03249    return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN;
03250 }

static int num_available_members ( struct call_queue q  )  [static]

Get the number of members available to accept a call.

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

Definition at line 3113 of file app_queue.c.

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

Referenced by compare_weight(), and is_our_turn().

03114 {
03115    struct member *mem;
03116    int avl = 0;
03117    struct ao2_iterator mem_iter;
03118 
03119    mem_iter = ao2_iterator_init(q->members, 0);
03120    while ((mem = ao2_iterator_next(&mem_iter))) {
03121       switch (mem->status) {
03122       case AST_DEVICE_INUSE:
03123          if (!q->ringinuse)
03124             break;
03125          /* else fall through */
03126       case AST_DEVICE_NOT_INUSE:
03127       case AST_DEVICE_ONHOLD:
03128       case AST_DEVICE_RINGINUSE:
03129       case AST_DEVICE_RINGING:
03130       case AST_DEVICE_UNKNOWN:
03131          if (!mem->paused) {
03132             avl++;
03133          }
03134          break;
03135       }
03136       ao2_ref(mem, -1);
03137 
03138       /* If autofill is not enabled or if the queue's strategy is ringall, then
03139        * we really don't care about the number of available members so much as we
03140        * do that there is at least one available.
03141        *
03142        * In fact, we purposely will return from this function stating that only
03143        * one member is available if either of those conditions hold. That way,
03144        * functions which determine what action to take based on the number of available
03145        * members will operate properly. The reasoning is that even if multiple
03146        * members are available, only the head caller can actually be serviced.
03147        */
03148       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
03149          break;
03150       }
03151    }
03152    ao2_iterator_destroy(&mem_iter);
03153 
03154    return avl;
03155 }

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

Definition at line 2046 of file app_queue.c.

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

Referenced by queue_set_param().

02047 {
02048    char *value_copy = ast_strdupa(value);
02049    char *option = NULL;
02050    while ((option = strsep(&value_copy, ","))) {
02051       if (!strcasecmp(option, "paused")) {
02052          *empty |= QUEUE_EMPTY_PAUSED;
02053       } else if (!strcasecmp(option, "penalty")) {
02054          *empty |= QUEUE_EMPTY_PENALTY;
02055       } else if (!strcasecmp(option, "inuse")) {
02056          *empty |= QUEUE_EMPTY_INUSE;
02057       } else if (!strcasecmp(option, "ringing")) {
02058          *empty |= QUEUE_EMPTY_RINGING;
02059       } else if (!strcasecmp(option, "invalid")) {
02060          *empty |= QUEUE_EMPTY_INVALID;
02061       } else if (!strcasecmp(option, "wrapup")) {
02062          *empty |= QUEUE_EMPTY_WRAPUP;
02063       } else if (!strcasecmp(option, "unavailable")) {
02064          *empty |= QUEUE_EMPTY_UNAVAILABLE;
02065       } else if (!strcasecmp(option, "unknown")) {
02066          *empty |= QUEUE_EMPTY_UNKNOWN;
02067       } else if (!strcasecmp(option, "loose")) {
02068          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
02069       } else if (!strcasecmp(option, "strict")) {
02070          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
02071       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
02072          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
02073       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
02074          *empty = 0;
02075       } else {
02076          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
02077       }
02078    }
02079 }

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

Definition at line 2783 of file app_queue.c.

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

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

02784 {
02785    int res;
02786 
02787    if (ast_strlen_zero(filename)) {
02788       return 0;
02789    }
02790 
02791    if (!ast_fileexists(filename, NULL, chan->language)) {
02792       return 0;
02793    }
02794 
02795    ast_stopstream(chan);
02796 
02797    res = ast_streamfile(chan, filename, chan->language);
02798    if (!res)
02799       res = ast_waitstream(chan, AST_DIGIT_ANY);
02800 
02801    ast_stopstream(chan);
02802 
02803    return res;
02804 }

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

PauseQueueMember application.

Definition at line 6063 of file app_queue.c.

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

Referenced by load_module().

06064 {
06065    char *parse;
06066    AST_DECLARE_APP_ARGS(args,
06067       AST_APP_ARG(queuename);
06068       AST_APP_ARG(interface);
06069       AST_APP_ARG(options);
06070       AST_APP_ARG(reason);
06071    );
06072 
06073    if (ast_strlen_zero(data)) {
06074       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
06075       return -1;
06076    }
06077 
06078    parse = ast_strdupa(data);
06079 
06080    AST_STANDARD_APP_ARGS(args, parse);
06081 
06082    if (ast_strlen_zero(args.interface)) {
06083       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
06084       return -1;
06085    }
06086 
06087    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
06088       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
06089       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
06090       return 0;
06091    }
06092 
06093    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
06094 
06095    return 0;
06096 }

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

QueueLog application.

Definition at line 6254 of file app_queue.c.

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

Referenced by load_module().

06255 {
06256    char *parse;
06257 
06258    AST_DECLARE_APP_ARGS(args,
06259       AST_APP_ARG(queuename);
06260       AST_APP_ARG(uniqueid);
06261       AST_APP_ARG(membername);
06262       AST_APP_ARG(event);
06263       AST_APP_ARG(params);
06264    );
06265 
06266    if (ast_strlen_zero(data)) {
06267       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
06268       return -1;
06269    }
06270 
06271    parse = ast_strdupa(data);
06272 
06273    AST_STANDARD_APP_ARGS(args, parse);
06274 
06275    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
06276        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
06277       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
06278       return -1;
06279    }
06280 
06281    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
06282       "%s", args.params ? args.params : "");
06283 
06284    return 0;
06285 }

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

Definition at line 1400 of file app_queue.c.

References CMP_MATCH, and CMP_STOP.

Referenced by load_module().

01401 {
01402    struct call_queue *q = obj, *q2 = arg;
01403    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
01404 }

static int queue_delme_members_decrement_followers ( void *  obj,
void *  arg,
int  flag 
) [static]

Definition at line 1429 of file app_queue.c.

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

Referenced by reload_single_queue().

01430 {
01431    struct member *mem = obj;
01432    struct call_queue *queue = arg;
01433    int rrpos = mem->queuepos;
01434 
01435    if (mem->delme) {
01436       ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &rrpos);
01437    }
01438 
01439    return 0;
01440 }

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

References call_queue::announcefrequency, ao2_container_count(), args, AST_APP_ARG, ast_assert, ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_FIRST, ast_log(), ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::chan, copy_rules(), queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, ast_party_caller::id, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::members, queue_ent::min_penalty, queue_ent::moh, ast_party_id::number, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, QUEUE_CONTINUE, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, queue_unref(), record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), set_queue_variables(), queue_ent::start, status, stop, ast_party_number::str, queue_ent::tries, try_calling(), update_qe_rule(), update_realtime_members(), url, ast_party_number::valid, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

06329 {
06330    int res=-1;
06331    int ringing=0;
06332    const char *user_priority;
06333    const char *max_penalty_str;
06334    const char *min_penalty_str;
06335    int prio;
06336    int qcontinue = 0;
06337    int max_penalty, min_penalty;
06338    enum queue_result reason = QUEUE_UNKNOWN;
06339    /* whether to exit Queue application after the timeout hits */
06340    int tries = 0;
06341    int noption = 0;
06342    char *parse;
06343    int makeannouncement = 0;
06344    int position = 0;
06345    AST_DECLARE_APP_ARGS(args,
06346       AST_APP_ARG(queuename);
06347       AST_APP_ARG(options);
06348       AST_APP_ARG(url);
06349       AST_APP_ARG(announceoverride);
06350       AST_APP_ARG(queuetimeoutstr);
06351       AST_APP_ARG(agi);
06352       AST_APP_ARG(macro);
06353       AST_APP_ARG(gosub);
06354       AST_APP_ARG(rule);
06355       AST_APP_ARG(position);
06356    );
06357    /* Our queue entry */
06358    struct queue_ent qe = { 0 };
06359    
06360    if (ast_strlen_zero(data)) {
06361       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
06362       return -1;
06363    }
06364    
06365    parse = ast_strdupa(data);
06366    AST_STANDARD_APP_ARGS(args, parse);
06367 
06368    /* Setup our queue entry */
06369    qe.start = time(NULL);
06370 
06371    /* set the expire time based on the supplied timeout; */
06372    if (!ast_strlen_zero(args.queuetimeoutstr))
06373       qe.expire = qe.start + atoi(args.queuetimeoutstr);
06374    else
06375       qe.expire = 0;
06376 
06377    /* Get the priority from the variable ${QUEUE_PRIO} */
06378    ast_channel_lock(chan);
06379    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
06380    if (user_priority) {
06381       if (sscanf(user_priority, "%30d", &prio) == 1) {
06382          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
06383       } else {
06384          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
06385             user_priority, chan->name);
06386          prio = 0;
06387       }
06388    } else {
06389       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
06390       prio = 0;
06391    }
06392 
06393    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
06394 
06395    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
06396       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
06397          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
06398       } else {
06399          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
06400             max_penalty_str, chan->name);
06401          max_penalty = INT_MAX;
06402       }
06403    } else {
06404       max_penalty = INT_MAX;
06405    }
06406 
06407    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
06408       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
06409          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
06410       } else {
06411          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
06412             min_penalty_str, chan->name);
06413          min_penalty = INT_MAX;
06414       }
06415    } else {
06416       min_penalty = INT_MAX;
06417    }
06418    ast_channel_unlock(chan);
06419 
06420    if (args.options && (strchr(args.options, 'r')))
06421       ringing = 1;
06422 
06423    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
06424       qe.ring_when_ringing = 1;
06425    }
06426 
06427    if (args.options && (strchr(args.options, 'c')))
06428       qcontinue = 1;
06429 
06430    if (args.position) {
06431       position = atoi(args.position);
06432       if (position < 0) {
06433          ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
06434          position = 0;
06435       }
06436    }
06437 
06438    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
06439       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
06440 
06441    qe.chan = chan;
06442    qe.prio = prio;
06443    qe.max_penalty = max_penalty;
06444    qe.min_penalty = min_penalty;
06445    qe.last_pos_said = 0;
06446    qe.last_pos = 0;
06447    qe.last_periodic_announce_time = time(NULL);
06448    qe.last_periodic_announce_sound = 0;
06449    qe.valid_digits = 0;
06450    if (join_queue(args.queuename, &qe, &reason, position)) {
06451       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
06452       set_queue_result(chan, reason);
06453       return 0;
06454    }
06455    ast_assert(qe.parent != NULL);
06456 
06457    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
06458       S_OR(args.url, ""),
06459       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
06460       qe.opos);
06461    copy_rules(&qe, args.rule);
06462    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
06463 check_turns:
06464    if (ringing) {
06465       ast_indicate(chan, AST_CONTROL_RINGING);
06466    } else {
06467       ast_moh_start(chan, qe.moh, NULL);
06468    }
06469 
06470    /* This is the wait loop for callers 2 through maxlen */
06471    res = wait_our_turn(&qe, ringing, &reason);
06472    if (res) {
06473       goto stop;
06474    }
06475 
06476    makeannouncement = 0;
06477 
06478    for (;;) {
06479       /* This is the wait loop for the head caller*/
06480       /* To exit, they may get their call answered; */
06481       /* they may dial a digit from the queue context; */
06482       /* or, they may timeout. */
06483 
06484       /* Leave if we have exceeded our queuetimeout */
06485       if (qe.expire && (time(NULL) >= qe.expire)) {
06486          record_abandoned(&qe);
06487          reason = QUEUE_TIMEOUT;
06488          res = 0;
06489          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
06490             qe.pos, qe.opos, (long) time(NULL) - qe.start);
06491          break;
06492       }
06493 
06494       if (makeannouncement) {
06495          /* Make a position announcement, if enabled */
06496          if (qe.parent->announcefrequency)
06497             if ((res = say_position(&qe,ringing)))
06498                goto stop;
06499       }
06500       makeannouncement = 1;
06501 
06502       /* Make a periodic announcement, if enabled */
06503       if (qe.parent->periodicannouncefrequency)
06504          if ((res = say_periodic_announcement(&qe,ringing)))
06505             goto stop;
06506    
06507       /* Leave if we have exceeded our queuetimeout */
06508       if (qe.expire && (time(NULL) >= qe.expire)) {
06509          record_abandoned(&qe);
06510          reason = QUEUE_TIMEOUT;
06511          res = 0;
06512          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06513          break;
06514       }
06515 
06516       /* see if we need to move to the next penalty level for this queue */
06517       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
06518          update_qe_rule(&qe);
06519       }
06520 
06521       /* Try calling all queue members for 'timeout' seconds */
06522       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
06523       if (res) {
06524          goto stop;
06525       }
06526 
06527       if (qe.parent->leavewhenempty) {
06528          int status = 0;
06529          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty, 0))) {
06530             record_abandoned(&qe);
06531             reason = QUEUE_LEAVEEMPTY;
06532             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
06533             res = 0;
06534             break;
06535          }
06536       }
06537 
06538       /* exit after 'timeout' cycle if 'n' option enabled */
06539       if (noption && tries >= ao2_container_count(qe.parent->members)) {
06540          ast_verb(3, "Exiting on time-out cycle\n");
06541          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06542          record_abandoned(&qe);
06543          reason = QUEUE_TIMEOUT;
06544          res = 0;
06545          break;
06546       }
06547 
06548       
06549       /* Leave if we have exceeded our queuetimeout */
06550       if (qe.expire && (time(NULL) >= qe.expire)) {
06551          record_abandoned(&qe);
06552          reason = QUEUE_TIMEOUT;
06553          res = 0;
06554          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06555          break;
06556       }
06557 
06558       /* If using dynamic realtime members, we should regenerate the member list for this queue */
06559       update_realtime_members(qe.parent);
06560       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
06561       res = wait_a_bit(&qe);
06562       if (res)
06563          goto stop;
06564 
06565       /* Since this is a priority queue and
06566        * it is not sure that we are still at the head
06567        * of the queue, go and check for our turn again.
06568        */
06569       if (!is_our_turn(&qe)) {
06570          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
06571          goto check_turns;
06572       }
06573    }
06574 
06575 stop:
06576    if (res) {
06577       if (res < 0) {
06578          if (!qe.handled) {
06579             record_abandoned(&qe);
06580             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
06581                "%d|%d|%ld", qe.pos, qe.opos,
06582                (long) time(NULL) - qe.start);
06583             res = -1;
06584          } else if (qcontinue) {
06585             reason = QUEUE_CONTINUE;
06586             res = 0;
06587          }
06588       } else if (qe.valid_digits) {
06589          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
06590             "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) time(NULL) - qe.start);
06591       }
06592    }
06593 
06594    /* Don't allow return code > 0 */
06595    if (res >= 0) {
06596       res = 0; 
06597       if (ringing) {
06598          ast_indicate(chan, -1);
06599       } else {
06600          ast_moh_stop(chan);
06601       }        
06602       ast_stopstream(chan);
06603    }
06604 
06605    set_queue_variables(qe.parent, qe.chan);
06606 
06607    leave_queue(&qe);
06608    if (reason != QUEUE_UNKNOWN)
06609       set_queue_result(chan, reason);
06610 
06611    /*
06612     * every queue_ent is given a reference to it's parent
06613     * call_queue when it joins the queue.  This ref must be taken
06614     * away right before the queue_ent is destroyed.  In this case
06615     * the queue_ent is about to be returned on the stack
06616     */
06617    qe.parent = queue_unref(qe.parent);
06618 
06619    return res;
06620 }

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

Check if a given queue exists.

Definition at line 6674 of file app_queue.c.

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

06675 {
06676    struct call_queue *q;
06677 
06678    buf[0] = '\0';
06679 
06680    if (ast_strlen_zero(data)) {
06681       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06682       return -1;
06683    }
06684    q = load_realtime_queue(data);
06685    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06686    if (q) {
06687       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06688    }
06689 
06690    return 0;
06691 }

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

Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.

Definition at line 6893 of file app_queue.c.

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

06894 {
06895    int penalty;
06896    AST_DECLARE_APP_ARGS(args,
06897       AST_APP_ARG(queuename);
06898       AST_APP_ARG(interface);
06899    );
06900    /* Make sure the returned value on error is NULL. */
06901    buf[0] = '\0';
06902 
06903    if (ast_strlen_zero(data)) {
06904       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06905       return -1;
06906    }
06907 
06908    AST_STANDARD_APP_ARGS(args, data);
06909 
06910    if (args.argc < 2) {
06911       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06912       return -1;
06913    }
06914 
06915    penalty = get_member_penalty (args.queuename, args.interface);
06916    
06917    if (penalty >= 0) /* remember that buf is already '\0' */
06918       snprintf (buf, len, "%d", penalty);
06919 
06920    return 0;
06921 }

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

Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.

Definition at line 6924 of file app_queue.c.

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

06925 {
06926    int penalty;
06927    AST_DECLARE_APP_ARGS(args,
06928       AST_APP_ARG(queuename);
06929       AST_APP_ARG(interface);
06930    );
06931 
06932    if (ast_strlen_zero(data)) {
06933       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06934       return -1;
06935    }
06936 
06937    AST_STANDARD_APP_ARGS(args, data);
06938 
06939    if (args.argc < 2) {
06940       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06941       return -1;
06942    }
06943 
06944    penalty = atoi(value);
06945 
06946    if (ast_strlen_zero(args.interface)) {
06947       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06948       return -1;
06949    }
06950 
06951    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06952    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06953       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06954       return -1;
06955    }
06956 
06957    return 0;
06958 }

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

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

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

Definition at line 6698 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, member::lastcall, load_realtime_queue(), LOG_ERROR, LOG_WARNING, call_queue::members, member::paused, queue_t_unref, member::status, and call_queue::wrapuptime.

06699 {
06700    int count = 0;
06701    struct member *m;
06702    struct ao2_iterator mem_iter;
06703    struct call_queue *q;
06704    char *option;
06705 
06706    if (ast_strlen_zero(data)) {
06707       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06708       return -1;
06709    }
06710 
06711    if ((option = strchr(data, ',')))
06712       *option++ = '\0';
06713    else
06714       option = "logged";
06715    if ((q = load_realtime_queue(data))) {
06716       ao2_lock(q);
06717       if (!strcasecmp(option, "logged")) {
06718          mem_iter = ao2_iterator_init(q->members, 0);
06719          while ((m = ao2_iterator_next(&mem_iter))) {
06720             /* Count the agents who are logged in and presently answering calls */
06721             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06722                count++;
06723             }
06724             ao2_ref(m, -1);
06725          }
06726          ao2_iterator_destroy(&mem_iter);
06727       } else if (!strcasecmp(option, "free")) {
06728          mem_iter = ao2_iterator_init(q->members, 0);
06729          while ((m = ao2_iterator_next(&mem_iter))) {
06730             /* Count the agents who are logged in and presently answering calls */
06731             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06732                count++;
06733             }
06734             ao2_ref(m, -1);
06735          }
06736          ao2_iterator_destroy(&mem_iter);
06737       } else if (!strcasecmp(option, "ready")) {
06738          time_t now;
06739          time(&now);
06740          mem_iter = ao2_iterator_init(q->members, 0);
06741          while ((m = ao2_iterator_next(&mem_iter))) {
06742             /* Count the agents who are logged in, not paused and not wrapping up */
06743             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06744                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06745                count++;
06746             }
06747             ao2_ref(m, -1);
06748          }
06749          ao2_iterator_destroy(&mem_iter);
06750       } else /* must be "count" */
06751          count = ao2_container_count(q->members);
06752       ao2_unlock(q);
06753       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06754    } else
06755       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06756 
06757    snprintf(buf, len, "%d", count);
06758 
06759    return 0;
06760 }

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

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

Return values:
number of members
-1 on error

Definition at line 6767 of file app_queue.c.

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

06768 {
06769    int count = 0;
06770    struct member *m;
06771    struct call_queue *q;
06772    struct ao2_iterator mem_iter;
06773    static int depflag = 1;
06774 
06775    if (depflag) {
06776       depflag = 0;
06777       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");
06778    }
06779 
06780    if (ast_strlen_zero(data)) {
06781       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06782       return -1;
06783    }
06784    
06785    if ((q = load_realtime_queue(data))) {
06786       ao2_lock(q);
06787       mem_iter = ao2_iterator_init(q->members, 0);
06788       while ((m = ao2_iterator_next(&mem_iter))) {
06789          /* Count the agents who are logged in and presently answering calls */
06790          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06791             count++;
06792          }
06793          ao2_ref(m, -1);
06794       }
06795       ao2_iterator_destroy(&mem_iter);
06796       ao2_unlock(q);
06797       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06798    } else
06799       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06800 
06801    snprintf(buf, len, "%d", count);
06802 
06803    return 0;
06804 }

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

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

Definition at line 6843 of file app_queue.c.

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

06844 {
06845    struct call_queue *q, tmpq = {
06846       .name = data,  
06847    };
06848    struct member *m;
06849 
06850    /* Ensure an otherwise empty list doesn't return garbage */
06851    buf[0] = '\0';
06852 
06853    if (ast_strlen_zero(data)) {
06854       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06855       return -1;
06856    }
06857 
06858    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06859       int buflen = 0, count = 0;
06860       struct ao2_iterator mem_iter;
06861 
06862       ao2_lock(q);
06863       mem_iter = ao2_iterator_init(q->members, 0);
06864       while ((m = ao2_iterator_next(&mem_iter))) {
06865          /* strcat() is always faster than printf() */
06866          if (count++) {
06867             strncat(buf + buflen, ",", len - buflen - 1);
06868             buflen++;
06869          }
06870          strncat(buf + buflen, m->interface, len - buflen - 1);
06871          buflen += strlen(m->interface);
06872          /* Safeguard against overflow (negative length) */
06873          if (buflen >= len - 2) {
06874             ao2_ref(m, -1);
06875             ast_log(LOG_WARNING, "Truncating list\n");
06876             break;
06877          }
06878          ao2_ref(m, -1);
06879       }
06880       ao2_iterator_destroy(&mem_iter);
06881       ao2_unlock(q);
06882       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06883    } else
06884       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06885 
06886    /* We should already be terminated, but let's make sure. */
06887    buf[len - 1] = '\0';
06888 
06889    return 0;
06890 }

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

Definition at line 7019 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, member::membername, call_queue::members, member::paused, queue_t_unref, and queues.

07020 {
07021    struct ast_module_user *lu;
07022    struct call_queue *q;
07023    struct member *cur;
07024    struct ao2_iterator queue_iter;
07025    struct ao2_iterator mem_iter;
07026    char tmp[128] = "";
07027    char *buffer;
07028 
07029    AST_DECLARE_APP_ARGS(args,
07030       AST_APP_ARG(queue);
07031       AST_APP_ARG(interface);
07032    );
07033 
07034    AST_STANDARD_APP_ARGS(args, data);
07035    if (ast_strlen_zero(args.interface)) {
07036       ast_log(LOG_WARNING, "This function requires an interface name.\n");
07037       return -1;
07038    } 
07039    lu = ast_module_user_add(chan);
07040 
07041    buffer = ast_malloc(len);
07042    buffer[0]='\0';
07043 
07044    queue_iter = ao2_iterator_init(queues,0);
07045    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07046       ao2_lock(q);
07047       if (ast_strlen_zero(args.queue) ||
07048             (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue)))
07049             ) {
07050             /* Iterate over queue members */
07051             mem_iter = ao2_iterator_init(q->members, 0);
07052             while ((cur = ao2_iterator_next(&mem_iter))) {
07053                   if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) {
07054                         if (!ast_strlen_zero(args.queue)) {
07055                               ast_copy_string(buffer, cur->paused?"1":"0", len);
07056                         } else {
07057                               snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, cur->paused?"1":"0");
07058                               strncat(buffer, tmp, sizeof(tmp));
07059                         }
07060                         ao2_ref(cur, -1);
07061                         break;
07062                   }
07063                   ao2_ref(cur, -1);
07064             }
07065             ao2_iterator_destroy(&mem_iter);
07066       }
07067       ao2_unlock(q);
07068       queue_t_unref(q, "Done with iterator");
07069    }
07070    ao2_iterator_destroy(&queue_iter);
07071    ast_copy_string(buf, buffer, len);
07072    ast_free(buffer);
07073 
07074    ast_module_user_remove(lu);
07075    return 0;
07076 }

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

Definition at line 6960 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_devstate2str(), ast_free, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, member::membername, call_queue::members, queue_t_unref, queues, and member::status.

06961 {
06962    struct ast_module_user *lu;
06963    struct call_queue *q;
06964    struct member *cur;
06965    struct ao2_iterator queue_iter;
06966    struct ao2_iterator mem_iter;
06967    char tmp[128] = "";
06968    char *buffer;
06969 
06970    AST_DECLARE_APP_ARGS(args,
06971       AST_APP_ARG(queue);
06972       AST_APP_ARG(interface);
06973    );
06974 
06975    AST_STANDARD_APP_ARGS(args, data);
06976    if (ast_strlen_zero(args.interface)) {
06977       ast_log(LOG_WARNING, "This function requires an interface name.\n");
06978       return -1;
06979    } 
06980    lu = ast_module_user_add(chan);
06981 
06982    buffer = ast_malloc(len);
06983    buffer[0]='\0';
06984 
06985    queue_iter = ao2_iterator_init(queues,0);
06986    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06987       ao2_lock(q);
06988       if (ast_strlen_zero(args.queue) ||
06989             (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue)))
06990             ) {
06991          /* Iterate over queue members */
06992          mem_iter = ao2_iterator_init(q->members, 0);
06993          while ((cur = ao2_iterator_next(&mem_iter))) {
06994             if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) {
06995                if (!ast_strlen_zero(args.queue)) {
06996                   ast_copy_string(buffer, ast_devstate2str(cur->status), len);
06997                } else {
06998                   snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, ast_devstate2str(cur->status));
06999                               strncat(buffer, tmp, sizeof(tmp));
07000                }
07001                ao2_ref(cur, -1);
07002                continue;
07003             }
07004             ao2_ref(cur, -1);
07005          }
07006          ao2_iterator_destroy(&mem_iter);
07007       }
07008       ao2_unlock(q);
07009       queue_t_unref(q, "Done with iterator");
07010    }
07011    ao2_iterator_destroy(&queue_iter);
07012    ast_copy_string(buf, buffer, len);
07013    ast_free(buffer);
07014 
07015    ast_module_user_remove(lu);
07016    return 0;
07017 }

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

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

Definition at line 6807 of file app_queue.c.

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

06808 {
06809    int count = 0;
06810    struct call_queue *q, tmpq = {
06811       .name = data,  
06812    };
06813    struct ast_variable *var = NULL;
06814 
06815    buf[0] = '\0';
06816    
06817    if (ast_strlen_zero(data)) {
06818       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06819       return -1;
06820    }
06821 
06822    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06823       ao2_lock(q);
06824       count = q->count;
06825       ao2_unlock(q);
06826       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06827    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06828       /* if the queue is realtime but was not found in memory, this
06829        * means that the queue had been deleted from memory since it was 
06830        * "dead." This means it has a 0 waiting count
06831        */
06832       count = 0;
06833       ast_variables_destroy(var);
06834    } else
06835       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06836 
06837    snprintf(buf, len, "%d", count);
06838 
06839    return 0;
06840 }

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

create interface var with all queue details.

Return values:
0 on success
-1 on error

Definition at line 6627 of file app_queue.c.

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

06628 {
06629    int res = -1;
06630    struct call_queue *q, tmpq = {
06631       .name = data,  
06632    };
06633 
06634    char interfacevar[256] = "";
06635    float sl = 0;
06636 
06637    if (ast_strlen_zero(data)) {
06638       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06639       return -1;
06640    }
06641 
06642    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06643       ao2_lock(q);
06644       if (q->setqueuevar) {
06645          sl = 0;
06646          res = 0;
06647 
06648          if (q->callscompleted > 0) {
06649             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06650          }
06651 
06652          snprintf(interfacevar, sizeof(interfacevar),
06653             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06654             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06655 
06656          pbx_builtin_setvar_multiple(chan, interfacevar);
06657       }
06658 
06659       ao2_unlock(q);
06660       queue_t_unref(q, "Done with QUEUE() function");
06661    } else {
06662       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06663    }
06664 
06665    snprintf(buf, len, "%d", res);
06666 
06667    return 0;
06668 }

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

Definition at line 1393 of file app_queue.c.

References ast_str_case_hash().

Referenced by load_module().

01394 {
01395    const struct call_queue *q = obj;
01396 
01397    return ast_str_case_hash(q->name);
01398 }

static int queue_member_decrement_followers ( void *  obj,
void *  arg,
int  flag 
) [static]

Definition at line 1411 of file app_queue.c.

References member::queuepos.

Referenced by queue_delme_members_decrement_followers(), and queue_member_follower_removal().

01412 {
01413    struct member *mem = obj;
01414    int *decrement_followers_after = arg;
01415 
01416    if (mem->queuepos > *decrement_followers_after) {
01417       mem->queuepos--;
01418    }
01419 
01420    return 0;
01421 }

static void queue_member_follower_removal ( struct call_queue queue,
struct member mem 
) [static]

Definition at line 1447 of file app_queue.c.

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

Referenced by member_remove_from_queue().

01448 {
01449    int pos = mem->queuepos;
01450 
01451    /* If the position being removed is less than the current place in the queue, reduce the queue position by one so that we don't skip the member
01452     * who would have been next otherwise. */
01453    if (pos < queue->rrpos) {
01454       queue->rrpos--;
01455    }
01456 
01457    ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &pos);
01458 }

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

Definition at line 1472 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01473 {
01474    ao2_ref(q, 1);
01475    return q;
01476 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

07180 {
07181    const char *general_val = NULL;
07182    queue_debug = 0;
07183    if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
07184       queue_debug = ast_true(general_val);
07185    queue_persistent_members = 0;
07186    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
07187       queue_persistent_members = ast_true(general_val);
07188    autofill_default = 0;
07189    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
07190       autofill_default = ast_true(general_val);
07191    montype_default = 0;
07192    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
07193       if (!strcasecmp(general_val, "mixmonitor"))
07194          montype_default = 1;
07195    }
07196    update_cdr = 0;
07197    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
07198       update_cdr = ast_true(general_val);
07199    shared_lastcall = 0;
07200    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
07201       shared_lastcall = ast_true(general_val);
07202 }

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

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

Referenced by find_queue_by_name_rt(), and reload_single_queue().

02090 {
02091    if (!strcasecmp(param, "musicclass") || 
02092       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
02093       ast_string_field_set(q, moh, val);
02094    } else if (!strcasecmp(param, "announce")) {
02095       ast_string_field_set(q, announce, val);
02096    } else if (!strcasecmp(param, "context")) {
02097       ast_string_field_set(q, context, val);
02098    } else if (!strcasecmp(param, "timeout")) {
02099       q->timeout = atoi(val);
02100       if (q->timeout < 0)
02101          q->timeout = DEFAULT_TIMEOUT;
02102    } else if (!strcasecmp(param, "ringinuse")) {
02103       q->ringinuse = ast_true(val);
02104    } else if (!strcasecmp(param, "setinterfacevar")) {
02105       q->setinterfacevar = ast_true(val);
02106    } else if (!strcasecmp(param, "setqueuevar")) {
02107       q->setqueuevar = ast_true(val);
02108    } else if (!strcasecmp(param, "setqueueentryvar")) {
02109       q->setqueueentryvar = ast_true(val);
02110    } else if (!strcasecmp(param, "monitor-format")) {
02111       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
02112    } else if (!strcasecmp(param, "membermacro")) {
02113       ast_string_field_set(q, membermacro, val);
02114    } else if (!strcasecmp(param, "membergosub")) {
02115       ast_string_field_set(q, membergosub, val);
02116    } else if (!strcasecmp(param, "queue-youarenext")) {
02117       ast_string_field_set(q, sound_next, val);
02118    } else if (!strcasecmp(param, "queue-thereare")) {
02119       ast_string_field_set(q, sound_thereare, val);
02120    } else if (!strcasecmp(param, "queue-callswaiting")) {
02121       ast_string_field_set(q, sound_calls, val);
02122    } else if (!strcasecmp(param, "queue-quantity1")) {
02123       ast_string_field_set(q, queue_quantity1, val);
02124    } else if (!strcasecmp(param, "queue-quantity2")) {
02125       ast_string_field_set(q, queue_quantity2, val);
02126    } else if (!strcasecmp(param, "queue-holdtime")) {
02127       ast_string_field_set(q, sound_holdtime, val);
02128    } else if (!strcasecmp(param, "queue-minutes")) {
02129       ast_string_field_set(q, sound_minutes, val);
02130    } else if (!strcasecmp(param, "queue-minute")) {
02131       ast_string_field_set(q, sound_minute, val);
02132    } else if (!strcasecmp(param, "queue-seconds")) {
02133       ast_string_field_set(q, sound_seconds, val);
02134    } else if (!strcasecmp(param, "queue-thankyou")) {
02135       ast_string_field_set(q, sound_thanks, val);
02136    } else if (!strcasecmp(param, "queue-callerannounce")) {
02137       ast_string_field_set(q, sound_callerannounce, val);
02138    } else if (!strcasecmp(param, "queue-reporthold")) {
02139       ast_string_field_set(q, sound_reporthold, val);
02140    } else if (!strcasecmp(param, "announce-frequency")) {
02141       q->announcefrequency = atoi(val);
02142    } else if (!strcasecmp(param, "announce-to-first-user")) {
02143       q->announce_to_first_user = ast_true(val);
02144    } else if (!strcasecmp(param, "min-announce-frequency")) {
02145       q->minannouncefrequency = atoi(val);
02146       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
02147    } else if (!strcasecmp(param, "announce-round-seconds")) {
02148       q->roundingseconds = atoi(val);
02149       /* Rounding to any other values just doesn't make sense... */
02150       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
02151          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
02152          if (linenum >= 0) {
02153             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
02154                "using 0 instead for queue '%s' at line %d of queues.conf\n",
02155                val, param, q->name, linenum);
02156          } else {
02157             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
02158                "using 0 instead for queue '%s'\n", val, param, q->name);
02159          }
02160          q->roundingseconds=0;
02161       }
02162    } else if (!strcasecmp(param, "announce-holdtime")) {
02163       if (!strcasecmp(val, "once"))
02164          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
02165       else if (ast_true(val))
02166          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
02167       else
02168          q->announceholdtime = 0;
02169    } else if (!strcasecmp(param, "announce-position")) {
02170       if (!strcasecmp(val, "limit"))
02171          q->announceposition = ANNOUNCEPOSITION_LIMIT;
02172       else if (!strcasecmp(val, "more"))
02173          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
02174       else if (ast_true(val))
02175          q->announceposition = ANNOUNCEPOSITION_YES;
02176       else
02177          q->announceposition = ANNOUNCEPOSITION_NO;
02178    } else if (!strcasecmp(param, "announce-position-limit")) {
02179       q->announcepositionlimit = atoi(val);
02180    } else if (!strcasecmp(param, "periodic-announce")) {
02181       if (strchr(val, ',')) {
02182          char *s, *buf = ast_strdupa(val);
02183          unsigned int i = 0;
02184 
02185          while ((s = strsep(&buf, ",|"))) {
02186             if (!q->sound_periodicannounce[i])
02187                q->sound_periodicannounce[i] = ast_str_create(16);
02188             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
02189             i++;
02190             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
02191                break;
02192          }
02193          q->numperiodicannounce = i;
02194       } else {
02195          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
02196          q->numperiodicannounce = 1;
02197       }
02198    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
02199       q->periodicannouncefrequency = atoi(val);
02200    } else if (!strcasecmp(param, "relative-periodic-announce")) {
02201       q->relativeperiodicannounce = ast_true(val);
02202    } else if (!strcasecmp(param, "random-periodic-announce")) {
02203       q->randomperiodicannounce = ast_true(val);
02204    } else if (!strcasecmp(param, "retry")) {
02205       q->retry = atoi(val);
02206       if (q->retry <= 0)
02207          q->retry = DEFAULT_RETRY;
02208    } else if (!strcasecmp(param, "wrapuptime")) {
02209       q->wrapuptime = atoi(val);
02210    } else if (!strcasecmp(param, "penaltymemberslimit")) {
02211       if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
02212          q->penaltymemberslimit = 0;
02213       }
02214    } else if (!strcasecmp(param, "autofill")) {
02215       q->autofill = ast_true(val);
02216    } else if (!strcasecmp(param, "monitor-type")) {
02217       if (!strcasecmp(val, "mixmonitor"))
02218          q->montype = 1;
02219    } else if (!strcasecmp(param, "autopause")) {
02220       q->autopause = autopause2int(val);
02221    } else if (!strcasecmp(param, "maxlen")) {
02222       q->maxlen = atoi(val);
02223       if (q->maxlen < 0)
02224          q->maxlen = 0;
02225    } else if (!strcasecmp(param, "ringlimit")) {
02226       q->ringlimit = atoi(val);
02227       if (q->ringlimit < 0)
02228          q->ringlimit = 0;
02229    } else if (!strcasecmp(param, "servicelevel")) {
02230       q->servicelevel= atoi(val);
02231    } else if (!strcasecmp(param, "strategy")) {
02232       int strategy;
02233 
02234       /* We are a static queue and already have set this, no need to do it again */
02235       if (failunknown) {
02236          return;
02237       }
02238       strategy = strat2int(val);
02239       if (strategy < 0) {
02240          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02241             val, q->name);
02242          q->strategy = QUEUE_STRATEGY_RINGALL;
02243       }
02244       if (strategy == q->strategy) {
02245          return;
02246       }
02247       if (strategy == QUEUE_STRATEGY_LINEAR) {
02248          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
02249          return;
02250       }
02251       q->strategy = strategy;
02252    } else if (!strcasecmp(param, "joinempty")) {
02253       parse_empty_options(val, &q->joinempty, 1);
02254    } else if (!strcasecmp(param, "leavewhenempty")) {
02255       parse_empty_options(val, &q->leavewhenempty, 0);
02256    } else if (!strcasecmp(param, "eventmemberstatus")) {
02257       q->maskmemberstatus = !ast_true(val);
02258    } else if (!strcasecmp(param, "eventwhencalled")) {
02259       if (!strcasecmp(val, "vars")) {
02260          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
02261       } else {
02262          q->eventwhencalled = ast_true(val) ? 1 : 0;
02263       }
02264    } else if (!strcasecmp(param, "reportholdtime")) {
02265       q->reportholdtime = ast_true(val);
02266    } else if (!strcasecmp(param, "memberdelay")) {
02267       q->memberdelay = atoi(val);
02268    } else if (!strcasecmp(param, "weight")) {
02269       q->weight = atoi(val);
02270    } else if (!strcasecmp(param, "timeoutrestart")) {
02271       q->timeoutrestart = ast_true(val);
02272    } else if (!strcasecmp(param, "defaultrule")) {
02273       ast_string_field_set(q, defaultrule, val);
02274    } else if (!strcasecmp(param, "timeoutpriority")) {
02275       if (!strcasecmp(val, "conf")) {
02276          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
02277       } else {
02278          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
02279       }
02280    } else if (failunknown) {
02281       if (linenum >= 0) {
02282          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
02283             q->name, param, linenum);
02284       } else {
02285          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
02286       }
02287    }
02288 }

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

Definition at line 7838 of file app_queue.c.

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

07839 {
07840    switch ( cmd ) {
07841    case CLI_INIT:
07842       e->command = "queue show";
07843       e->usage =
07844          "Usage: queue show\n"
07845          "       Provides summary information on a specified queue.\n";
07846       return NULL;
07847    case CLI_GENERATE:
07848       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07849    }
07850 
07851    return __queues_show(NULL, a->fd, a->argc, a->argv);
07852 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4676 of file app_queue.c.

References ast_free.

04677 {
04678    struct queue_transfer_ds *qtds = data;
04679    ast_free(qtds);
04680 }

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

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

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

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

Definition at line 4699 of file app_queue.c.

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

04700 {
04701    struct queue_transfer_ds *qtds = data;
04702    struct queue_ent *qe = qtds->qe;
04703    struct member *member = qtds->member;
04704    time_t callstart = qtds->starttime;
04705    int callcompletedinsl = qtds->callcompletedinsl;
04706    struct ast_datastore *datastore;
04707 
04708    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04709             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04710             (long) (time(NULL) - callstart), qe->opos);
04711 
04712    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04713    
04714    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04715    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04716       ast_channel_datastore_remove(old_chan, datastore);
04717       /* Datastore is freed in try_calling() */
04718    } else {
04719       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04720    }
04721 }

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

Definition at line 1478 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec(), and queues_data_provider_get().

01479 {
01480    ao2_ref(q, -1);
01481    return NULL;
01482 }

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

Definition at line 9008 of file app_queue.c.

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

09010 {
09011    struct ao2_iterator i;
09012    struct call_queue *queue, *queue_realtime = NULL;
09013    struct ast_config *cfg;
09014    char *queuename;
09015 
09016    /* load realtime queues. */
09017    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
09018    if (cfg) {
09019       for (queuename = ast_category_browse(cfg, NULL);
09020             !ast_strlen_zero(queuename);
09021             queuename = ast_category_browse(cfg, queuename)) {
09022          if ((queue = load_realtime_queue(queuename))) {
09023             queue_unref(queue);
09024          }
09025       }
09026       ast_config_destroy(cfg);
09027    }
09028 
09029    /* static queues. */
09030    i = ao2_iterator_init(queues, 0);
09031    while ((queue = ao2_iterator_next(&i))) {
09032       ao2_lock(queue);
09033       if (queue->realtime) {
09034          queue_realtime = load_realtime_queue(queue->name);
09035          if (!queue_realtime) {
09036             ao2_unlock(queue);
09037             queue_unref(queue);
09038             continue;
09039          }
09040          queue_unref(queue_realtime);
09041       }
09042 
09043       queues_data_provider_get_helper(search, data_root, queue);
09044       ao2_unlock(queue);
09045       queue_unref(queue);
09046    }
09047    ao2_iterator_destroy(&i);
09048 
09049    return 0;
09050 }

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

Definition at line 8902 of file app_queue.c.

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

Referenced by queues_data_provider_get().

08904 {
08905    struct ao2_iterator im;
08906    struct member *member;
08907    struct queue_ent *qe;
08908    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08909    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08910 
08911    data_queue = ast_data_add_node(data_root, "queue");
08912    if (!data_queue) {
08913       return;
08914    }
08915 
08916    ast_data_add_structure(call_queue, data_queue, queue);
08917 
08918    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08919    ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08920 
08921    /* announce position */
08922    enum_node = ast_data_add_node(data_queue, "announceposition");
08923    if (!enum_node) {
08924       return;
08925    }
08926    switch (queue->announceposition) {
08927    case ANNOUNCEPOSITION_LIMIT:
08928       ast_data_add_str(enum_node, "text", "limit");
08929       break;
08930    case ANNOUNCEPOSITION_MORE_THAN:
08931       ast_data_add_str(enum_node, "text", "more");
08932       break;
08933    case ANNOUNCEPOSITION_YES:
08934       ast_data_add_str(enum_node, "text", "yes");
08935       break;
08936    case ANNOUNCEPOSITION_NO:
08937       ast_data_add_str(enum_node, "text", "no");
08938       break;
08939    default:
08940       ast_data_add_str(enum_node, "text", "unknown");
08941       break;
08942    }
08943    ast_data_add_int(enum_node, "value", queue->announceposition);
08944 
08945    /* add queue members */
08946    im = ao2_iterator_init(queue->members, 0);
08947    while ((member = ao2_iterator_next(&im))) {
08948       if (!data_members) {
08949          data_members = ast_data_add_node(data_queue, "members");
08950          if (!data_members) {
08951             ao2_ref(member, -1);
08952             continue;
08953          }
08954       }
08955 
08956       data_member = ast_data_add_node(data_members, "member");
08957       if (!data_member) {
08958          ao2_ref(member, -1);
08959          continue;
08960       }
08961 
08962       ast_data_add_structure(member, data_member, member);
08963 
08964       ao2_ref(member, -1);
08965    }
08966    ao2_iterator_destroy(&im);
08967 
08968    /* include the callers inside the result. */
08969    if (queue->head) {
08970       for (qe = queue->head; qe; qe = qe->next) {
08971          if (!data_callers) {
08972             data_callers = ast_data_add_node(data_queue, "callers");
08973             if (!data_callers) {
08974                continue;
08975             }
08976          }
08977 
08978          data_caller = ast_data_add_node(data_callers, "caller");
08979          if (!data_caller) {
08980             continue;
08981          }
08982 
08983          ast_data_add_structure(queue_ent, data_caller, qe);
08984 
08985          /* add the caller channel. */
08986          data_caller_channel = ast_data_add_node(data_caller, "channel");
08987          if (!data_caller_channel) {
08988             continue;
08989          }
08990 
08991          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08992       }
08993    }
08994 
08995    /* if this queue doesn't match remove the added queue. */
08996    if (!ast_data_search_match(search, data_queue)) {
08997       ast_data_remove_node(data_root, data_queue);
08998    }
08999 }

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

Definition at line 2989 of file app_queue.c.

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

Referenced by try_calling().

02990 {
02991    int oldvalue;
02992 
02993    /* Calculate holdtime using an exponential average */
02994    /* Thanks to SRT for this contribution */
02995    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02996 
02997    ao2_lock(qe->parent);
02998    oldvalue = qe->parent->holdtime;
02999    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
03000    ao2_unlock(qe->parent);
03001 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 3702 of file app_queue.c.

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

Referenced by queue_exec(), and try_calling().

03703 {
03704    set_queue_variables(qe->parent, qe->chan);
03705    ao2_lock(qe->parent);
03706    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03707       "Queue: %s\r\n"
03708       "Uniqueid: %s\r\n"
03709       "Position: %d\r\n"
03710       "OriginalPosition: %d\r\n"
03711       "HoldTime: %d\r\n",
03712       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03713 
03714    qe->parent->callsabandoned++;
03715    ao2_unlock(qe->parent);
03716 }

static int reload ( void   )  [static]

Definition at line 9188 of file app_queue.c.

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

09189 {
09190    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
09191    ast_unload_realtime("queue_members");
09192    reload_handler(1, &mask, NULL);
09193    return 0;
09194 }

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

The command center for all reload operations.

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

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

Definition at line 7558 of file app_queue.c.

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

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

07559 {
07560    int res = 0;
07561 
07562    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
07563       res |= reload_queue_rules(reload);
07564    }
07565    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
07566       res |= clear_stats(queuename);
07567    }
07568    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
07569       res |= reload_queues(reload, mask, queuename);
07570    }
07571    return res;
07572 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 5968 of file app_queue.c.

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

Referenced by load_module().

05969 {
05970    char *cur_ptr;
05971    const char *queue_name;
05972    char *member;
05973    char *interface;
05974    char *membername = NULL;
05975    char *state_interface;
05976    char *penalty_tok;
05977    int penalty = 0;
05978    char *paused_tok;
05979    int paused = 0;
05980    struct ast_db_entry *db_tree;
05981    struct ast_db_entry *entry;
05982    struct call_queue *cur_queue;
05983    char *queue_data;
05984 
05985    /* Each key in 'pm_family' is the name of a queue */
05986    db_tree = ast_db_gettree(pm_family, NULL);
05987    for (entry = db_tree; entry; entry = entry->next) {
05988 
05989       queue_name = entry->key + strlen(pm_family) + 2;
05990 
05991       {
05992          struct call_queue tmpq = {
05993             .name = queue_name,
05994          };
05995          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05996       }  
05997 
05998       if (!cur_queue)
05999          cur_queue = load_realtime_queue(queue_name);
06000 
06001       if (!cur_queue) {
06002          /* If the queue no longer exists, remove it from the
06003           * database */
06004          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
06005          ast_db_del(pm_family, queue_name);
06006          continue;
06007       } 
06008 
06009       if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
06010          queue_t_unref(cur_queue, "Expire reload reference");
06011          continue;
06012       }
06013 
06014       cur_ptr = queue_data;
06015       while ((member = strsep(&cur_ptr, ",|"))) {
06016          if (ast_strlen_zero(member))
06017             continue;
06018 
06019          interface = strsep(&member, ";");
06020          penalty_tok = strsep(&member, ";");
06021          paused_tok = strsep(&member, ";");
06022          membername = strsep(&member, ";");
06023          state_interface = strsep(&member, ";");
06024 
06025          if (!penalty_tok) {
06026             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
06027             break;
06028          }
06029          penalty = strtol(penalty_tok, NULL, 10);
06030          if (errno == ERANGE) {
06031             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
06032             break;
06033          }
06034          
06035          if (!paused_tok) {
06036             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
06037             break;
06038          }
06039          paused = strtol(paused_tok, NULL, 10);
06040          if ((errno == ERANGE) || paused < 0 || paused > 1) {
06041             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
06042             break;
06043          }
06044 
06045          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
06046          
06047          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
06048             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
06049             break;
06050          }
06051       }
06052       queue_t_unref(cur_queue, "Expire reload reference");
06053       ast_free(queue_data);
06054    }
06055 
06056    if (db_tree) {
06057       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
06058       ast_db_freetree(db_tree);
06059    }
06060 }

static int reload_queue_rules ( int  reload  )  [static]

Reload the rules defined in queuerules.conf.

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

Definition at line 7130 of file app_queue.c.

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

Referenced by reload_handler().

07131 {
07132    struct ast_config *cfg;
07133    struct rule_list *rl_iter, *new_rl;
07134    struct penalty_rule *pr_iter;
07135    char *rulecat = NULL;
07136    struct ast_variable *rulevar = NULL;
07137    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07138    
07139    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
07140       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
07141       return AST_MODULE_LOAD_SUCCESS;
07142    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07143       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
07144       return AST_MODULE_LOAD_SUCCESS;
07145    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07146       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
07147       return AST_MODULE_LOAD_SUCCESS;
07148    }
07149 
07150    AST_LIST_LOCK(&rule_lists);
07151    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
07152       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
07153          ast_free(pr_iter);
07154       ast_free(rl_iter);
07155    }
07156    while ((rulecat = ast_category_browse(cfg, rulecat))) {
07157       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
07158          AST_LIST_UNLOCK(&rule_lists);
07159          ast_config_destroy(cfg);
07160          return AST_MODULE_LOAD_FAILURE;
07161       } else {
07162          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
07163          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
07164          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
07165             if(!strcasecmp(rulevar->name, "penaltychange"))
07166                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
07167             else
07168                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
07169       }
07170    }
07171    AST_LIST_UNLOCK(&rule_lists);
07172 
07173    ast_config_destroy(cfg);
07174 
07175    return AST_MODULE_LOAD_SUCCESS;
07176 }

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

reload the queues.conf file

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

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

Definition at line 7463 of file app_queue.c.

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

Referenced by reload_handler().

07464 {
07465    struct ast_config *cfg;
07466    char *cat;
07467    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07468    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
07469    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
07470 
07471    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
07472       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
07473       return -1;
07474    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07475       return 0;
07476    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07477       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
07478       return -1;
07479    }
07480 
07481    /* We've made it here, so it looks like we're doing operations on all queues. */
07482    ao2_lock(queues);
07483 
07484    /* Mark all queues as dead for the moment if we're reloading queues.
07485     * For clarity, we could just be reloading members, in which case we don't want to mess
07486     * with the other queue parameters at all*/
07487    if (queue_reload) {
07488       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
07489    }
07490 
07491    if (member_reload) {
07492       ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename);
07493    }
07494 
07495    /* Chug through config file */
07496    cat = NULL;
07497    while ((cat = ast_category_browse(cfg, cat)) ) {
07498       if (!strcasecmp(cat, "general") && queue_reload) {
07499          queue_set_global_params(cfg);
07500          continue;
07501       }
07502       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
07503          reload_single_queue(cfg, mask, cat);
07504    }
07505 
07506    ast_config_destroy(cfg);
07507    /* Unref all the dead queues if we were reloading queues */
07508    if (queue_reload) {
07509       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
07510    }
07511    ao2_unlock(queues);
07512    return 0;
07513 }

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

reload information pertaining to a single member

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

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

Definition at line 7212 of file app_queue.c.

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

Referenced by reload_single_queue().

07213 {
07214    char *membername, *interface, *state_interface, *tmp;
07215    char *parse;
07216    struct member *cur, *newm;
07217    struct member tmpmem;
07218    int penalty;
07219    AST_DECLARE_APP_ARGS(args,
07220       AST_APP_ARG(interface);
07221       AST_APP_ARG(penalty);
07222       AST_APP_ARG(membername);
07223       AST_APP_ARG(state_interface);
07224    );
07225 
07226    if (ast_strlen_zero(memberdata)) {
07227       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
07228       return;
07229    }
07230 
07231    /* Add a new member */
07232    parse = ast_strdupa(memberdata);
07233             
07234    AST_STANDARD_APP_ARGS(args, parse);
07235 
07236    interface = args.interface;
07237    if (!ast_strlen_zero(args.penalty)) {
07238       tmp = args.penalty;
07239       ast_strip(tmp);
07240       penalty = atoi(tmp);
07241       if (penalty < 0) {
07242          penalty = 0;
07243       }
07244    } else {
07245       penalty = 0;
07246    }
07247 
07248    if (!ast_strlen_zero(args.membername)) {
07249       membername = args.membername;
07250       ast_strip(membername);
07251    } else {
07252       membername = interface;
07253    }
07254 
07255    if (!ast_strlen_zero(args.state_interface)) {
07256       state_interface = args.state_interface;
07257       ast_strip(state_interface);
07258    } else {
07259       state_interface = interface;
07260    }
07261 
07262    /* Find the old position in the list */
07263    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
07264    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
07265 
07266    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
07267       if (cur) {
07268          /* Round Robin Queue Position must be copied if this is replacing an existing member */
07269          ao2_lock(q->members);
07270          newm->queuepos = cur->queuepos;
07271          ao2_link(q->members, newm);
07272          ao2_unlink(q->members, cur);
07273          ao2_unlock(q->members);
07274       } else {
07275          /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
07276          member_add_to_queue(q, newm);
07277       }
07278       ao2_ref(newm, -1);
07279    }
07280    newm = NULL;
07281 
07282    if (cur) {
07283       ao2_ref(cur, -1);
07284    }
07285 }

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

Reload information pertaining to a particular queue.

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

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

Definition at line 7319 of file app_queue.c.

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

Referenced by reload_queues().

07320 {
07321    int new;
07322    struct call_queue *q = NULL;
07323    /*We're defining a queue*/
07324    struct call_queue tmpq = {
07325       .name = queuename,
07326    };
07327    const char *tmpvar;
07328    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
07329    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
07330    int prev_weight = 0;
07331    struct ast_variable *var;
07332    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
07333       if (queue_reload) {
07334          /* Make one then */
07335          if (!(q = alloc_queue(queuename))) {
07336             return;
07337          }
07338       } else {
07339          /* Since we're not reloading queues, this means that we found a queue
07340           * in the configuration file which we don't know about yet. Just return.
07341           */
07342          return;
07343       }
07344       new = 1;
07345    } else {
07346       new = 0;
07347    }
07348    
07349    if (!new) {
07350       ao2_lock(q);
07351       prev_weight = q->weight ? 1 : 0;
07352    }
07353    /* Check if we already found a queue with this name in the config file */
07354    if (q->found) {
07355       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
07356       if (!new) {
07357          /* It should be impossible to *not* hit this case*/
07358          ao2_unlock(q);
07359       }
07360       queue_t_unref(q, "We exist! Expiring temporary pointer");
07361       return;
07362    }
07363    /* Due to the fact that the "linear" strategy will have a different allocation
07364     * scheme for queue members, we must devise the queue's strategy before other initializations.
07365     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
07366     * container used will have only a single bucket instead of the typical number.
07367     */
07368    if (queue_reload) {
07369       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
07370          q->strategy = strat2int(tmpvar);
07371          if (q->strategy < 0) {
07372             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
07373             tmpvar, q->name);
07374             q->strategy = QUEUE_STRATEGY_RINGALL;
07375          }
07376       } else {
07377          q->strategy = QUEUE_STRATEGY_RINGALL;
07378       }
07379       init_queue(q);
07380    }
07381    if (member_reload) {
07382       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
07383       q->found = 1;
07384    }
07385    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
07386       if (member_reload && !strcasecmp(var->name, "member")) {
07387          reload_single_member(var->value, q);
07388       } else if (queue_reload) {
07389          queue_set_param(q, var->name, var->value, var->lineno, 1);
07390       }
07391    }
07392    /* At this point, we've determined if the queue has a weight, so update use_weight
07393     * as appropriate
07394     */
07395    if (!q->weight && prev_weight) {
07396       ast_atomic_fetchadd_int(&use_weight, -1);
07397    }
07398    else if (q->weight && !prev_weight) {
07399       ast_atomic_fetchadd_int(&use_weight, +1);
07400    }
07401 
07402    /* Free remaining members marked as delme */
07403    if (member_reload) {
07404       ao2_lock(q->members);
07405       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q);
07406       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
07407       ao2_unlock(q->members);
07408    }
07409 
07410    if (new) {
07411       queues_t_link(queues, q, "Add queue to container");
07412    } else {
07413       ao2_unlock(q);
07414    }
07415    queue_t_unref(q, "Expiring creation reference");
07416 }

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

Remove member from queue.

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

Definition at line 5711 of file app_queue.c.

References ao2_find, ao2_lock, ao2_ref, ao2_t_find, ao2_unlock, ast_copy_string(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event, member_remove_from_queue(), member::membername, call_queue::members, OBJ_POINTER, queue_t_unref, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

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

05712 {
05713    struct call_queue *q, tmpq = {
05714       .name = queuename,   
05715    };
05716    struct member *mem, tmpmem;
05717    int res = RES_NOSUCHQUEUE;
05718 
05719    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05720    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05721       ao2_lock(q);
05722       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05723          /* XXX future changes should beware of this assumption!! */
05724          if (!mem->dynamic) {
05725             ao2_ref(mem, -1);
05726             ao2_unlock(q);
05727             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05728             return RES_NOT_DYNAMIC;
05729          }
05730          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05731             "Queue: %s\r\n"
05732             "Location: %s\r\n"
05733             "MemberName: %s\r\n",
05734             q->name, mem->interface, mem->membername);
05735          member_remove_from_queue(q, mem);
05736          ao2_ref(mem, -1);
05737 
05738          if (queue_persistent_members)
05739             dump_queue_members(q);
05740          
05741          res = RES_OKAY;
05742       } else {
05743          res = RES_EXISTS;
05744       }
05745       ao2_unlock(q);
05746       queue_t_unref(q, "Expiring temporary reference");
05747    }
05748 
05749    return res;
05750 }

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

Definition at line 7418 of file app_queue.c.

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

Referenced by reload_queues().

07419 {
07420    struct call_queue *q = obj;
07421    char *queuename = arg;
07422    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07423       q->found = 0;
07424 
07425    }
07426    return 0;
07427 }

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

Part 2 of ring_one.

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

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

Definition at line 3364 of file app_queue.c.

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ast_party_connected_line::ani, ast_party_caller::ani, ao2_lock, ao2_unlock, ast_channel::appl, ast_assert, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock_both, ast_channel_set_caller_event(), ast_channel_unlock, ast_connected_line_copy_from_caller(), ast_copy_string(), AST_FLAG_ANSWERED_ELSEWHERE, ast_log(), ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_set_flag, ast_string_field_set, ast_strlen_zero(), ast_verb, member::call_pending, ast_channel::caller, can_ring_entry(), queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_cdr::clid, ast_channel::connected, ast_channel::context, ast_channel::data, ast_cdr::dcontext, callattempt::dial_callerid_absent, dialcontext, ast_channel::dialed, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, ast_party_connected_line::id, ast_party_caller::id, callattempt::interface, ast_cdr::lastapp, ast_cdr::lastdata, queue_ent::linpos, LOG_DEBUG, LOG_NOTICE, ast_channel::macroexten, manager_event, callattempt::member, member_call_pending_clear(), member::membername, ast_party_id::name, ast_channel::nativeformats, ast_party_dialed::number, ast_party_id::number, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, ast_channel::redirecting, call_queue::ringinuse, call_queue::rrpos, S_COR, S_OR, ast_cdr::src, status, callattempt::stillgoing, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_party_dialed::transit_network_select, ast_cdr::userfield, ast_party_name::valid, ast_party_number::valid, vars2manager(), and ast_channel::whentohangup.

Referenced by ring_one().

03365 {
03366    int res;
03367    int status;
03368    char tech[256];
03369    char *location, *location2;
03370    const char *macrocontext, *macroexten;
03371    char pickupmark[256], chan[128];
03372 
03373    /* on entry here, we know that tmp->chan == NULL */
03374    if (!can_ring_entry(qe, tmp)) {
03375       tmp->stillgoing = 0;
03376       (*busies)++;
03377       return 0;
03378    }
03379    ast_assert(qe->parent->ringinuse || tmp->member->call_pending);
03380 
03381    ast_copy_string(tech, tmp->interface, sizeof(tech));
03382    if ((location = strchr(tech, '/')))
03383       *location++ = '\0';
03384    else
03385       location = "";
03386 
03387    /* Request the peer */
03388    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03389    if (!tmp->chan) {       /* If we can't, just go on to the next call */
03390       if (queue_debug)
03391          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
03392       ao2_lock(qe->parent);
03393       qe->parent->rrpos++;
03394       qe->linpos++;
03395       ao2_unlock(qe->parent);
03396 
03397       member_call_pending_clear(tmp->member);
03398 
03399       if (qe->chan->cdr) {
03400          ast_cdr_busy(qe->chan->cdr);
03401       }
03402       tmp->stillgoing = 0;
03403       (*busies)++;
03404       return 0;
03405    }
03406 
03407    ast_channel_lock_both(tmp->chan, qe->chan);
03408 
03409    if (qe->cancel_answered_elsewhere) {
03410       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03411    }
03412    tmp->chan->appl = "AppQueue";
03413    tmp->chan->data = "(Outgoing Line)";
03414    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03415 
03416    /* If the new channel has no callerid, try to guess what it should be */
03417    if (!tmp->chan->caller.id.number.valid) {
03418       if (qe->chan->connected.id.number.valid) {
03419          struct ast_party_caller caller;
03420 
03421          ast_party_caller_set_init(&caller, &tmp->chan->caller);
03422          caller.id = qe->chan->connected.id;
03423          caller.ani = qe->chan->connected.ani;
03424          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03425       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03426          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03427       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03428          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
03429       }
03430       tmp->dial_callerid_absent = 1;
03431    }
03432 
03433    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03434 
03435    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03436 
03437    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03438 
03439    /* Inherit specially named variables from parent channel */
03440    ast_channel_inherit_variables(qe->chan, tmp->chan);
03441    ast_channel_datastore_inherit(qe->chan, tmp->chan);
03442 
03443    /* Presense of ADSI CPE on outgoing channel follows ours */
03444    tmp->chan->adsicpe = qe->chan->adsicpe;
03445 
03446    /* Inherit context and extension */
03447    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03448    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03449    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03450    if (!ast_strlen_zero(macroexten))
03451       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03452    else
03453       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03454    if (ast_cdr_isset_unanswered()) {
03455       /* they want to see the unanswered dial attempts! */
03456       /* set up the CDR fields on all the CDRs to give sensical information */
03457       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
03458       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03459       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03460       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03461       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03462       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03463       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03464       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03465       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03466       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03467       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03468    }
03469 
03470    ast_channel_unlock(tmp->chan);
03471    ast_channel_unlock(qe->chan);
03472 
03473    /* Add a PICKUPMARK variable to ringing interface */
03474    if (option_debug > 2)
03475       ast_log(LOG_DEBUG, "chan %s, tech: %s, part %s\n", tmp->chan->name, tech, location);
03476    /* Delete DAHDI ring pattern in tech like DAHDI/1r2 */
03477    ast_copy_string(chan, location, sizeof(chan));
03478    if (!strncasecmp(tech, "dahdi", 5)) {
03479       if((chan[0] > '0') && (chan[0] <= '9')) {
03480          if ((location2 = strchr(chan, 'r')))
03481             *location2++ = '\0';
03482       }
03483    }
03484    snprintf(pickupmark, sizeof(pickupmark), "%s/%s", tech, chan);
03485    pbx_builtin_setvar_helper(tmp->chan, "PICKUPMARK", pickupmark);
03486 
03487    /* Place the call, but don't wait on the answer */
03488    if ((res = ast_call(tmp->chan, location, 0))) {
03489       /* Again, keep going even if there's an error */
03490       ast_verb(3, "Couldn't call %s\n", tmp->interface);
03491       do_hang(tmp);
03492       member_call_pending_clear(tmp->member);
03493       ++*busies;
03494       return 0;
03495    }
03496 
03497    if (qe->parent->eventwhencalled) {
03498       char vars[2048];
03499 
03500       ast_channel_lock_both(tmp->chan, qe->chan);
03501 
03502       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03503          "Queue: %s\r\n"
03504          "AgentCalled: %s\r\n"
03505          "AgentName: %s\r\n"
03506          "ChannelCalling: %s\r\n"
03507          "DestinationChannel: %s\r\n"
03508          "CallerIDNum: %s\r\n"
03509          "CallerIDName: %s\r\n"
03510          "ConnectedLineNum: %s\r\n"
03511          "ConnectedLineName: %s\r\n"
03512          "Context: %s\r\n"
03513          "Extension: %s\r\n"
03514          "Priority: %d\r\n"
03515          "Uniqueid: %s\r\n"
03516          "%s",
03517          qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03518          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
03519          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
03520          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
03521          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
03522          qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03523          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03524 
03525       ast_channel_unlock(tmp->chan);
03526       ast_channel_unlock(qe->chan);
03527 
03528       ast_verb(3, "Called %s\n", tmp->interface);
03529    }
03530 
03531    member_call_pending_clear(tmp->member);
03532    return 1;
03533 }

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

Place a call to a queue member.

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

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

Definition at line 3561 of file app_queue.c.

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

Referenced by try_calling(), and wait_for_answer().

03562 {
03563    int ret = 0;
03564 
03565    while (ret == 0) {
03566       struct callattempt *best = find_best(outgoing);
03567       if (!best) {
03568          ast_debug(1, "Nobody left to try ringing in queue\n");
03569          break;
03570       }
03571       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03572          struct callattempt *cur;
03573          /* Ring everyone who shares this best metric (for ringall) */
03574          for (cur = outgoing; cur; cur = cur->q_next) {
03575             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03576                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03577                ret |= ring_entry(qe, cur, busies);
03578             }
03579          }
03580       } else {
03581          /* Ring just the best channel */
03582          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03583          ret = ring_entry(qe, best, busies);
03584       }
03585       
03586       /* If we have timed out, break out */
03587       if (qe->expire && (time(NULL) >= qe->expire)) {
03588          ast_debug(1, "Queue timed out while ringing members.\n");
03589          ret = 0;
03590          break;
03591       }
03592    }
03593 
03594    return ret;
03595 }

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

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

Definition at line 3719 of file app_queue.c.

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

Referenced by wait_for_answer().

03720 {
03721    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03722 
03723    /* Stop ringing, and resume MOH if specified */
03724    if (qe->ring_when_ringing) {
03725       ast_indicate(qe->chan, -1);
03726       ast_moh_start(qe->chan, qe->moh, NULL);
03727    }
03728 
03729    if (qe->parent->eventwhencalled) {
03730       char vars[2048];
03731 
03732       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03733                   "Queue: %s\r\n"
03734                   "Uniqueid: %s\r\n"
03735                   "Channel: %s\r\n"
03736                   "Member: %s\r\n"
03737                   "MemberName: %s\r\n"
03738                   "Ringtime: %d\r\n"
03739                   "%s",
03740                   qe->parent->name,
03741                   qe->chan->uniqueid,
03742                   qe->chan->name,
03743                   interface,
03744                   membername,
03745                   rnatime,
03746                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03747    }
03748    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03749    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03750       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03751          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03752             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03753                interface, qe->parent->name);
03754          } else {
03755             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03756          }
03757       } else {
03758          /* If queue autopause is mode all, just don't send any queue to stop.
03759          * the function will stop in all queues */
03760          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03761             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03762                   interface, qe->parent->name);
03763          } else {
03764                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03765          }
03766       }
03767    }
03768    return;
03769 }

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

RemoveQueueMember application.

Definition at line 6135 of file app_queue.c.

References args, AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

06136 {
06137    int res=-1;
06138    char *parse, *temppos = NULL;
06139    AST_DECLARE_APP_ARGS(args,
06140       AST_APP_ARG(queuename);
06141       AST_APP_ARG(interface);
06142    );
06143 
06144 
06145    if (ast_strlen_zero(data)) {
06146       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
06147       return -1;
06148    }
06149 
06150    parse = ast_strdupa(data);
06151 
06152    AST_STANDARD_APP_ARGS(args, parse);
06153 
06154    if (ast_strlen_zero(args.interface)) {
06155       args.interface = ast_strdupa(chan->name);
06156       temppos = strrchr(args.interface, '-');
06157       if (temppos)
06158          *temppos = '\0';
06159    }
06160 
06161    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
06162 
06163    switch (remove_from_queue(args.queuename, args.interface)) {
06164    case RES_OKAY:
06165       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
06166       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
06167       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
06168       res = 0;
06169       break;
06170    case RES_EXISTS:
06171       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
06172       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
06173       res = 0;
06174       break;
06175    case RES_NOSUCHQUEUE:
06176       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
06177       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
06178       res = 0;
06179       break;
06180    case RES_NOT_DYNAMIC:
06181       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
06182       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
06183       res = 0;
06184       break;
06185    }
06186 
06187    return res;
06188 }

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

Find rt member record to update otherwise create one.

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

Definition at line 2324 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and update_realtime_members().

02325 {
02326    struct member *m;
02327    struct ao2_iterator mem_iter;
02328    int penalty = 0;
02329    int paused  = 0;
02330    int found = 0;
02331 
02332    if (ast_strlen_zero(rt_uniqueid)) {
02333       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02334       return;
02335    }
02336 
02337    if (penalty_str) {
02338       penalty = atoi(penalty_str);
02339       if (penalty < 0)
02340          penalty = 0;
02341    }
02342 
02343    if (paused_str) {
02344       paused = atoi(paused_str);
02345       if (paused < 0)
02346          paused = 0;
02347    }
02348 
02349    /* Find member by realtime uniqueid and update */
02350    mem_iter = ao2_iterator_init(q->members, 0);
02351    while ((m = ao2_iterator_next(&mem_iter))) {
02352       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02353          m->dead = 0;   /* Do not delete this one. */
02354          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02355          if (paused_str)
02356             m->paused = paused;
02357          if (strcasecmp(state_interface, m->state_interface)) {
02358             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02359          }     
02360          m->penalty = penalty;
02361          found = 1;
02362          ao2_ref(m, -1);
02363          break;
02364       }
02365       ao2_ref(m, -1);
02366    }
02367    ao2_iterator_destroy(&mem_iter);
02368 
02369    /* Create a new member */
02370    if (!found) {
02371       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02372          m->dead = 0;
02373          m->realtime = 1;
02374          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02375          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02376          member_add_to_queue(q, m);
02377          ao2_ref(m, -1);
02378          m = NULL;
02379       }
02380    }
02381 }

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

Playback announcement to queued members if period has elapsed.

Definition at line 3646 of file app_queue.c.

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

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

03647 {
03648    int res = 0;
03649    time_t now;
03650 
03651    /* Get the current time */
03652    time(&now);
03653 
03654    /* Check to see if it is time to announce */
03655    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03656       return 0;
03657 
03658    /* Stop the music on hold so we can play our own file */
03659    if (ringing)
03660       ast_indicate(qe->chan,-1);
03661    else
03662       ast_moh_stop(qe->chan);
03663 
03664    ast_verb(3, "Playing periodic announcement\n");
03665    
03666    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03667       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03668    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03669       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03670       qe->last_periodic_announce_sound = 0;
03671    }
03672    
03673    /* play the announcement */
03674    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03675 
03676    if (res > 0 && !valid_exit(qe, res))
03677       res = 0;
03678 
03679    /* Resume Music on Hold if the caller is going to stay in the queue */
03680    if (!res) {
03681       if (ringing)
03682          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03683       else
03684          ast_moh_start(qe->chan, qe->moh, NULL);
03685    }
03686 
03687    /* update last_periodic_announce_time */
03688    if (qe->parent->relativeperiodicannounce)
03689       time(&qe->last_periodic_announce_time);
03690    else
03691       qe->last_periodic_announce_time = now;
03692 
03693    /* Update the current periodic announcement to the next announcement */
03694    if (!qe->parent->randomperiodicannounce) {
03695       qe->last_periodic_announce_sound++;
03696    }
03697    
03698    return res;
03699 }

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

Definition at line 2845 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, AST_CONTROL_RINGING, AST_DIGIT_ANY, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verb, queue_ent::chan, call_queue::holdtime, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, queue_ent::start, and valid_exit().

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

02846 {
02847    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02848    int say_thanks = 1;
02849    time_t now;
02850 
02851    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02852    time(&now);
02853    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02854       return 0;
02855 
02856    /* If either our position has changed, or we are over the freq timer, say position */
02857    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02858       return 0;
02859 
02860    if (ringing) {
02861       ast_indicate(qe->chan,-1);
02862    } else {
02863       ast_moh_stop(qe->chan);
02864    }
02865 
02866    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02867       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02868       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02869       qe->pos <= qe->parent->announcepositionlimit))
02870          announceposition = 1;
02871 
02872 
02873    if (announceposition == 1) {
02874       /* Say we're next, if we are */
02875       if (qe->pos == 1) {
02876          res = play_file(qe->chan, qe->parent->sound_next);
02877          if (res)
02878             goto playout;
02879          else
02880             goto posout;
02881       } else {
02882          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02883             /* More than Case*/
02884             res = play_file(qe->chan, qe->parent->queue_quantity1);
02885             if (res)
02886                goto playout;
02887             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02888             if (res)
02889                goto playout;
02890          } else {
02891             /* Normal Case */
02892             res = play_file(qe->chan, qe->parent->sound_thereare);
02893             if (res)
02894                goto playout;
02895             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02896             if (res)
02897                goto playout;
02898          }
02899          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02900             /* More than Case*/
02901             res = play_file(qe->chan, qe->parent->queue_quantity2);
02902             if (res)
02903                goto playout;
02904          } else {
02905             res = play_file(qe->chan, qe->parent->sound_calls);
02906             if (res)
02907                goto playout;
02908          }
02909       }
02910    }
02911    /* Round hold time to nearest minute */
02912    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02913 
02914    /* If they have specified a rounding then round the seconds as well */
02915    if (qe->parent->roundingseconds) {
02916       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02917       avgholdsecs *= qe->parent->roundingseconds;
02918    } else {
02919       avgholdsecs = 0;
02920    }
02921 
02922    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02923 
02924    /* If the hold time is >1 min, if it's enabled, and if it's not
02925       supposed to be only once and we have already said it, say it */
02926     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02927         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02928         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02929       res = play_file(qe->chan, qe->parent->sound_holdtime);
02930       if (res)
02931          goto playout;
02932 
02933       if (avgholdmins >= 1) {
02934          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02935          if (res)
02936             goto playout;
02937 
02938          if (avgholdmins == 1) {
02939             res = play_file(qe->chan, qe->parent->sound_minute);
02940             if (res)
02941                goto playout;
02942          } else {
02943             res = play_file(qe->chan, qe->parent->sound_minutes);
02944             if (res)
02945                goto playout;
02946          }
02947       }
02948       if (avgholdsecs >= 1) {
02949          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02950          if (res)
02951             goto playout;
02952 
02953          res = play_file(qe->chan, qe->parent->sound_seconds);
02954          if (res)
02955             goto playout;
02956       }
02957    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02958       say_thanks = 0;
02959    }
02960 
02961 posout:
02962    if (qe->parent->announceposition) {
02963       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02964          qe->chan->name, qe->parent->name, qe->pos);
02965    }
02966    if (say_thanks) {
02967       res = play_file(qe->chan, qe->parent->sound_thanks);
02968    }
02969 playout:
02970 
02971    if ((res > 0 && !valid_exit(qe, res)))
02972       res = 0;
02973 
02974    /* Set our last_pos indicators */
02975    qe->last_pos = now;
02976    qe->last_pos_said = qe->pos;
02977 
02978    /* Don't restart music on hold if we're about to exit the caller from the queue */
02979    if (!res) {
02980       if (ringing) {
02981          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02982       } else {
02983          ast_moh_start(qe->chan, qe->moh, NULL);
02984       }
02985    }
02986    return res;
02987 }

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

Send out AMI message with member call completion status information.

Definition at line 4633 of file app_queue.c.

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

Referenced by try_calling().

04636 {
04637    const char *reason = NULL; /* silence dumb compilers */
04638 
04639    if (!qe->parent->eventwhencalled)
04640       return;
04641 
04642    switch (rsn) {
04643    case CALLER:
04644       reason = "caller";
04645       break;
04646    case AGENT:
04647       reason = "agent";
04648       break;
04649    case TRANSFER:
04650       reason = "transfer";
04651       break;
04652    }
04653 
04654    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04655       "Queue: %s\r\n"
04656       "Uniqueid: %s\r\n"
04657       "Channel: %s\r\n"
04658       "Member: %s\r\n"
04659       "MemberName: %s\r\n"
04660       "HoldTime: %ld\r\n"
04661       "TalkTime: %ld\r\n"
04662       "Reason: %s\r\n"
04663       "%s",
04664       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04665       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04666       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04667 }

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

Definition at line 5810 of file app_queue.c.

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

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

05811 {
05812    int found = 0;
05813    struct call_queue *q;
05814    struct member *mem;
05815    struct ao2_iterator queue_iter;
05816    int failed;
05817 
05818    /* Special event for when all queues are paused - individual events still generated */
05819    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05820    if (ast_strlen_zero(queuename))
05821       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05822 
05823    queue_iter = ao2_iterator_init(queues, 0);
05824    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05825       ao2_lock(q);
05826       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05827          if ((mem = interface_exists(q, interface))) {
05828             if (mem->paused == paused) {
05829                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05830             }
05831 
05832             failed = 0;
05833             if (mem->realtime) {
05834                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05835             }
05836          
05837             if (failed) {
05838                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05839                ao2_ref(mem, -1);
05840                ao2_unlock(q);
05841                queue_t_unref(q, "Done with iterator");
05842                continue;
05843             }  
05844             found++;
05845             mem->paused = paused;
05846 
05847             if (queue_persistent_members)
05848                dump_queue_members(q);
05849 
05850             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05851             
05852             if (!ast_strlen_zero(reason)) {
05853                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05854                   "Queue: %s\r\n"
05855                   "Location: %s\r\n"
05856                   "MemberName: %s\r\n"
05857                   "Paused: %d\r\n"
05858                   "Reason: %s\r\n",
05859                      q->name, mem->interface, mem->membername, paused, reason);
05860             } else {
05861                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05862                   "Queue: %s\r\n"
05863                   "Location: %s\r\n"
05864                   "MemberName: %s\r\n"
05865                   "Paused: %d\r\n",
05866                      q->name, mem->interface, mem->membername, paused);
05867             }
05868             ao2_ref(mem, -1);
05869          }
05870       }
05871       
05872       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05873          ao2_unlock(q);
05874          queue_t_unref(q, "Done with iterator");
05875          break;
05876       }
05877       
05878       ao2_unlock(q);
05879       queue_t_unref(q, "Done with iterator");
05880    }
05881    ao2_iterator_destroy(&queue_iter);
05882 
05883    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05884 }

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

Definition at line 5887 of file app_queue.c.

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

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

05888 {
05889    int foundinterface = 0, foundqueue = 0;
05890    struct call_queue *q;
05891    struct member *mem;
05892    struct ao2_iterator queue_iter;
05893 
05894    if (penalty < 0) {
05895       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05896       return RESULT_FAILURE;
05897    }
05898 
05899    queue_iter = ao2_iterator_init(queues, 0);
05900    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05901       ao2_lock(q);
05902       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05903          foundqueue++;
05904          if ((mem = interface_exists(q, interface))) {
05905             foundinterface++;
05906             mem->penalty = penalty;
05907             
05908             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05909             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05910                "Queue: %s\r\n"
05911                "Location: %s\r\n"
05912                "Penalty: %d\r\n",
05913                q->name, mem->interface, penalty);
05914             ao2_ref(mem, -1);
05915          }
05916       }
05917       ao2_unlock(q);
05918       queue_t_unref(q, "Done with iterator");
05919    }
05920    ao2_iterator_destroy(&queue_iter);
05921 
05922    if (foundinterface) {
05923       return RESULT_SUCCESS;
05924    } else if (!foundqueue) {
05925       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05926    } else {
05927       ast_log (LOG_ERROR, "Invalid interface\n");
05928    }  
05929 
05930    return RESULT_FAILURE;
05931 }

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

sets the QUEUESTATUS channel variable

Definition at line 1337 of file app_queue.c.

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

Referenced by queue_exec().

01338 {
01339    int i;
01340 
01341    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
01342       if (queue_results[i].id == res) {
01343          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
01344          return;
01345       }
01346    }
01347 }

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

Set variables of queue.

Definition at line 1486 of file app_queue.c.

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

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

01487 {
01488    char interfacevar[256]="";
01489    float sl = 0;
01490 
01491    ao2_lock(q);
01492 
01493    if (q->setqueuevar) {
01494       sl = 0;
01495       if (q->callscompleted > 0) 
01496          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01497 
01498       snprintf(interfacevar, sizeof(interfacevar),
01499          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01500          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01501 
01502       ao2_unlock(q);
01503    
01504       pbx_builtin_setvar_multiple(chan, interfacevar); 
01505    } else {
01506       ao2_unlock(q);
01507    }
01508 }

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

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

Definition at line 4738 of file app_queue.c.

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

Referenced by try_calling().

04739 {
04740    struct ast_datastore *ds;
04741    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04742 
04743    if (!qtds) {
04744       ast_log(LOG_WARNING, "Memory allocation error!\n");
04745       return NULL;
04746    }
04747 
04748    ast_channel_lock(qe->chan);
04749    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04750       ast_channel_unlock(qe->chan);
04751       ast_free(qtds);
04752       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04753       return NULL;
04754    }
04755 
04756    qtds->qe = qe;
04757    /* This member is refcounted in try_calling, so no need to add it here, too */
04758    qtds->member = member;
04759    qtds->starttime = starttime;
04760    qtds->callcompletedinsl = callcompletedinsl;
04761    ds->data = qtds;
04762    ast_channel_datastore_add(qe->chan, ds);
04763    ast_channel_unlock(qe->chan);
04764    return ds;
04765 }

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

Search for best metric and add to Linear queue.

Definition at line 3622 of file app_queue.c.

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

Referenced by try_calling().

03623 {
03624    struct callattempt *best = find_best(outgoing);
03625 
03626    if (best) {
03627       /* Ring just the best channel */
03628       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03629       qe->linpos = best->metric % 1000;
03630    } else {
03631       /* Just increment rrpos */
03632       if (qe->linwrapped) {
03633          /* No more channels, start over */
03634          qe->linpos = 0;
03635       } else {
03636          /* Prioritize next entry */
03637          qe->linpos++;
03638       }
03639    }
03640    qe->linwrapped = 0;
03641 
03642    return 0;
03643 }

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

Search for best metric and add to Round Robbin queue.

Definition at line 3598 of file app_queue.c.

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

Referenced by try_calling().

03599 {
03600    struct callattempt *best = find_best(outgoing);
03601 
03602    if (best) {
03603       /* Ring just the best channel */
03604       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03605       qe->parent->rrpos = best->metric % 1000;
03606    } else {
03607       /* Just increment rrpos */
03608       if (qe->parent->wrapped) {
03609          /* No more channels, start over */
03610          qe->parent->rrpos = 0;
03611       } else {
03612          /* Prioritize next entry */
03613          qe->parent->rrpos++;
03614       }
03615    }
03616    qe->parent->wrapped = 0;
03617 
03618    return 0;
03619 }

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

Definition at line 1361 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

01362 {
01363    int x;
01364 
01365    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01366       if (!strcasecmp(strategy, strategies[x].name))
01367          return strategies[x].strategy;
01368    }
01369 
01370    return -1;
01371 }

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

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

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

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

Definition at line 4819 of file app_queue.c.

References ast_channel::_softhangup, ast_channel::_state, AGENT, queue_ent::announce, ao2_alloc, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_asprintf, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_append(), ast_cdr_dup(), ast_cdr_failed(), AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_init(), ast_cdr_isset_unanswered(), ast_cdr_reset(), ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_datastore_alloc, ast_datastore_free(), ast_debug, AST_DIGIT_ANY, ast_exists_extension(), AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, AST_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_party_connected_line_copy(), ast_pbx_run_args(), ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), callattempt::block_connected_update, calc_metric(), callattempt_free(), CALLER, ast_channel::caller, member::calls, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, queue_end_bridge::chan, callattempt::chan, queue_ent::chan, ast_channel::connected, callattempt::connected, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, member::dynamic, end_bridge_callback(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, queue_ent::handled, hangupcalls(), ast_party_caller::id, ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), ast_cdr::linkedid, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event, callattempt::member, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, ast_cdr::next, ast_pbx_args::no_hangup_chan, ast_party_id::number, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_multiple(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), member::penalty, queue_ent::pending, play_file(), queue_ent::pos, ast_channel::priority, queue_end_bridge::q, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, queue_t_ref, member::realtime, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, S_COR, send_agent_complete(), call_queue::servicelevel, set_queue_variables(), call_queue::setinterfacevar, call_queue::setqueueentryvar, setup_transfer_datastore(), queue_ent::start, callattempt::stillgoing, store_next_lin(), store_next_rr(), ast_party_number::str, call_queue::strategy, ast_channel::tech, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, TRANSFER, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), ast_party_number::valid, vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.

Referenced by queue_exec().

04820 {
04821    struct member *cur;
04822    struct callattempt *outgoing = NULL; /* the list of calls we are building */
04823    int to, orig;
04824    char oldexten[AST_MAX_EXTENSION]="";
04825    char oldcontext[AST_MAX_CONTEXT]="";
04826    char queuename[256]="";
04827    char interfacevar[256]="";
04828    struct ast_channel *peer;
04829    struct ast_channel *which;
04830    struct callattempt *lpeer;
04831    struct member *member;
04832    struct ast_app *application;
04833    int res = 0, bridge = 0;
04834    int numbusies = 0;
04835    int x=0;
04836    char *announce = NULL;
04837    char digit = 0;
04838    time_t callstart;
04839    time_t now = time(NULL);
04840    struct ast_bridge_config bridge_config;
04841    char nondataquality = 1;
04842    char *agiexec = NULL;
04843    char *macroexec = NULL;
04844    char *gosubexec = NULL;
04845    const char *monitorfilename;
04846    const char *monitor_exec;
04847    const char *monitor_options;
04848    char tmpid[256], tmpid2[256];
04849    char meid[1024], meid2[1024];
04850    char mixmonargs[1512];
04851    struct ast_app *mixmonapp = NULL;
04852    char *p;
04853    char vars[2048];
04854    int forwardsallowed = 1;
04855    int block_connected_line = 0;
04856    int callcompletedinsl;
04857    struct ao2_iterator memi;
04858    struct ast_datastore *datastore, *transfer_ds;
04859    struct queue_end_bridge *queue_end_bridge = NULL;
04860 
04861    ast_channel_lock(qe->chan);
04862    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04863    ast_channel_unlock(qe->chan);
04864 
04865    memset(&bridge_config, 0, sizeof(bridge_config));
04866    tmpid[0] = 0;
04867    meid[0] = 0;
04868    time(&now);
04869 
04870    /* If we've already exceeded our timeout, then just stop
04871     * This should be extremely rare. queue_exec will take care
04872     * of removing the caller and reporting the timeout as the reason.
04873     */
04874    if (qe->expire && now >= qe->expire) {
04875       res = 0;
04876       goto out;
04877    }
04878       
04879    for (; options && *options; options++)
04880       switch (*options) {
04881       case 't':
04882          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04883          break;
04884       case 'T':
04885          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04886          break;
04887       case 'w':
04888          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04889          break;
04890       case 'W':
04891          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04892          break;
04893       case 'c':
04894          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04895          break;
04896       case 'd':
04897          nondataquality = 0;
04898          break;
04899       case 'h':
04900          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04901          break;
04902       case 'H':
04903          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04904          break;
04905       case 'k':
04906          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04907          break;
04908       case 'K':
04909          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04910          break;
04911       case 'n':
04912          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
04913             (*tries)++;
04914          else
04915             *tries = ao2_container_count(qe->parent->members);
04916          *noption = 1;
04917          break;
04918       case 'i':
04919          forwardsallowed = 0;
04920          break;
04921       case 'I':
04922          block_connected_line = 1;
04923          break;
04924       case 'x':
04925          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04926          break;
04927       case 'X':
04928          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04929          break;
04930       case 'C':
04931          qe->cancel_answered_elsewhere = 1;
04932          break;
04933       }
04934 
04935    /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 
04936       (this is mainly to support chan_local)
04937    */
04938    if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04939       qe->cancel_answered_elsewhere = 1;
04940    }
04941 
04942    ao2_lock(qe->parent);
04943    ast_debug(1, "%s is trying to call a queue member.\n",
04944                      qe->chan->name);
04945    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04946    if (!ast_strlen_zero(qe->announce))
04947       announce = qe->announce;
04948    if (!ast_strlen_zero(announceoverride))
04949       announce = announceoverride;
04950 
04951    memi = ao2_iterator_init(qe->parent->members, 0);
04952    while ((cur = ao2_iterator_next(&memi))) {
04953       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04954       struct ast_dialed_interface *di;
04955       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04956       if (!tmp) {
04957          ao2_ref(cur, -1);
04958          ao2_iterator_destroy(&memi);
04959          ao2_unlock(qe->parent);
04960          goto out;
04961       }
04962       if (!datastore) {
04963          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04964             callattempt_free(tmp);
04965             ao2_ref(cur, -1);
04966             ao2_iterator_destroy(&memi);
04967             ao2_unlock(qe->parent);
04968             goto out;
04969          }
04970          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04971          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04972             callattempt_free(tmp);
04973             ao2_ref(cur, -1);
04974             ao2_iterator_destroy(&memi);
04975             ao2_unlock(qe->parent);
04976             goto out;
04977          }
04978          datastore->data = dialed_interfaces;
04979          AST_LIST_HEAD_INIT(dialed_interfaces);
04980 
04981          ast_channel_lock(qe->chan);
04982          ast_channel_datastore_add(qe->chan, datastore);
04983          ast_channel_unlock(qe->chan);
04984       } else
04985          dialed_interfaces = datastore->data;
04986 
04987       AST_LIST_LOCK(dialed_interfaces);
04988       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04989          if (!strcasecmp(cur->interface, di->interface)) {
04990             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
04991                di->interface);
04992             break;
04993          }
04994       }
04995       AST_LIST_UNLOCK(dialed_interfaces);
04996 
04997       if (di) {
04998          callattempt_free(tmp);
04999          ao2_ref(cur, -1);
05000          continue;
05001       }
05002 
05003       /* It is always ok to dial a Local interface.  We only keep track of
05004        * which "real" interfaces have been dialed.  The Local channel will
05005        * inherit this list so that if it ends up dialing a real interface,
05006        * it won't call one that has already been called. */
05007       if (strncasecmp(cur->interface, "Local/", 6)) {
05008          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
05009             callattempt_free(tmp);
05010             ao2_ref(cur, -1);
05011             ao2_iterator_destroy(&memi);
05012             ao2_unlock(qe->parent);
05013             goto out;
05014          }
05015          strcpy(di->interface, cur->interface);
05016 
05017          AST_LIST_LOCK(dialed_interfaces);
05018          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
05019          AST_LIST_UNLOCK(dialed_interfaces);
05020       }
05021 
05022       /*
05023        * Seed the callattempt's connected line information with previously
05024        * acquired connected line info from the queued channel.  The
05025        * previously acquired connected line info could have been set
05026        * through the CONNECTED_LINE dialplan function.
05027        */
05028       ast_channel_lock(qe->chan);
05029       ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
05030       ast_channel_unlock(qe->chan);
05031 
05032       tmp->block_connected_update = block_connected_line;
05033       tmp->stillgoing = 1;
05034       tmp->member = cur;/* Place the reference for cur into callattempt. */
05035       tmp->lastcall = cur->lastcall;
05036       tmp->lastqueue = cur->lastqueue;
05037       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
05038       if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
05039          cur->ringcount = 0;
05040       }
05041       /* Special case: If we ring everyone, go ahead and ring them, otherwise
05042          just calculate their metric for the appropriate strategy */
05043       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
05044          /* Put them in the list of outgoing thingies...  We're ready now.
05045             XXX If we're forcibly removed, these outgoing calls won't get
05046             hung up XXX */
05047          tmp->q_next = outgoing;
05048          outgoing = tmp;      
05049          /* If this line is up, don't try anybody else */
05050          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
05051             break;
05052       } else {
05053          callattempt_free(tmp);
05054       }
05055    }
05056    ao2_iterator_destroy(&memi);
05057 
05058    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
05059       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
05060       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
05061          to = (qe->expire - now) * 1000;
05062       else
05063          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
05064    } else {
05065       /* Config timeout is higher priority thatn application timeout */
05066       if (qe->expire && qe->expire<=now) {
05067          to = 0;
05068       } else if (qe->parent->timeout) {
05069          to = qe->parent->timeout * 1000;
05070       } else {
05071          to = -1;
05072       }
05073    }
05074    orig = to;
05075    ++qe->pending;
05076    ++qe->tries;
05077    if (option_debug)
05078       ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
05079                   qe->chan->name, queuename, qe->tries);
05080    ao2_unlock(qe->parent);
05081    ring_one(qe, outgoing, &numbusies);
05082    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
05083       ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
05084       forwardsallowed, ringing);
05085    /* The ast_channel_datastore_remove() function could fail here if the
05086     * datastore was moved to another channel during a masquerade. If this is
05087     * the case, don't free the datastore here because later, when the channel
05088     * to which the datastore was moved hangs up, it will attempt to free this
05089     * datastore again, causing a crash
05090     */
05091    ast_channel_lock(qe->chan);
05092    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
05093       ast_datastore_free(datastore);
05094    }
05095    ast_channel_unlock(qe->chan);
05096    ao2_lock(qe->parent);
05097    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
05098       store_next_rr(qe, outgoing);
05099 
05100    }
05101    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
05102       store_next_lin(qe, outgoing);
05103    }
05104    ao2_unlock(qe->parent);
05105    peer = lpeer ? lpeer->chan : NULL;
05106    if (!peer) {
05107       qe->pending = 0;
05108       if (to) {
05109          /* Must gotten hung up */
05110          res = -1;
05111       } else {
05112          /* User exited by pressing a digit */
05113          res = digit;
05114       }
05115       if (option_debug && res == -1)
05116          ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
05117       if (ast_cdr_isset_unanswered()) {
05118          /* channel contains the name of one of the outgoing channels
05119             in its CDR; zero out this CDR to avoid a dual-posting */
05120          struct callattempt *o;
05121          for (o = outgoing; o; o = o->q_next) {
05122             if (!o->chan) {
05123                continue;
05124             }
05125             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
05126                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
05127                break;
05128             }
05129          }
05130       }
05131    } else { /* peer is valid */
05132       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
05133          we will always return with -1 so that it is hung up properly after the
05134          conversation.  */
05135       if (!strcmp(qe->chan->tech->type, "DAHDI"))
05136          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
05137       if (!strcmp(peer->tech->type, "DAHDI"))
05138          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
05139       /* Update parameters for the queue */
05140       time(&now);
05141       recalc_holdtime(qe, (now - qe->start));
05142       ao2_lock(qe->parent);
05143       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
05144       ao2_unlock(qe->parent);
05145       member = lpeer->member;
05146       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
05147       ao2_ref(member, 1);
05148       hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
05149       outgoing = NULL;
05150       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
05151          int res2;
05152 
05153          res2 = ast_autoservice_start(qe->chan);
05154          if (!res2) {
05155             if (qe->parent->memberdelay) {
05156                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
05157                res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
05158             }
05159             if (!res2 && announce) {
05160                if (play_file(peer, announce) < 0) {
05161                   ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name);
05162                }
05163             }
05164             if (!res2 && qe->parent->reportholdtime) {
05165                if (!play_file(peer, qe->parent->sound_reporthold)) {
05166                   int holdtime, holdtimesecs;
05167 
05168                   time(&now);
05169                   holdtime = abs((now - qe->start) / 60);
05170                   holdtimesecs = abs((now - qe->start) % 60);
05171                   if (holdtime > 0) {
05172                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
05173                      if (play_file(peer, qe->parent->sound_minutes) < 0) {
05174                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name);
05175                      }
05176                   }
05177                   if (holdtimesecs > 1) {
05178                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
05179                      if (play_file(peer, qe->parent->sound_seconds) < 0) {
05180                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name);
05181                      }
05182                   }
05183                }
05184             }
05185             ast_autoservice_stop(qe->chan);
05186          }
05187          if (ast_check_hangup(peer)) {
05188             /* Agent must have hung up */
05189             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
05190             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
05191             if (qe->parent->eventwhencalled)
05192                manager_event(EVENT_FLAG_AGENT, "AgentDump",
05193                      "Queue: %s\r\n"
05194                      "Uniqueid: %s\r\n"
05195                      "Channel: %s\r\n"
05196                      "Member: %s\r\n"
05197                      "MemberName: %s\r\n"
05198                      "%s",
05199                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
05200                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05201             ast_hangup(peer);
05202             ao2_ref(member, -1);
05203             goto out;
05204          } else if (ast_check_hangup(qe->chan)) {
05205             /* Caller must have hung up just before being connected */
05206             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
05207             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
05208             record_abandoned(qe);
05209             ast_hangup(peer);
05210             ao2_ref(member, -1);
05211             return -1;
05212          }
05213       }
05214       /* Stop music on hold */
05215       if (ringing)
05216          ast_indicate(qe->chan,-1);
05217       else
05218          ast_moh_stop(qe->chan);
05219       /* If appropriate, log that we have a destination channel */
05220       if (qe->chan->cdr)
05221          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
05222       /* Make sure channels are compatible */
05223       res = ast_channel_make_compatible(qe->chan, peer);
05224       if (res < 0) {
05225          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
05226          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
05227          record_abandoned(qe);
05228          ast_cdr_failed(qe->chan->cdr);
05229          ast_hangup(peer);
05230          ao2_ref(member, -1);
05231          return -1;
05232       }
05233 
05234       /* Play announcement to the caller telling it's his turn if defined */
05235       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
05236          if (play_file(qe->chan, qe->parent->sound_callerannounce))
05237             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
05238       }
05239 
05240       ao2_lock(qe->parent);
05241       /* if setinterfacevar is defined, make member variables available to the channel */
05242       /* use  pbx_builtin_setvar to set a load of variables with one call */
05243       if (qe->parent->setinterfacevar) {
05244          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
05245             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
05246          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
05247          pbx_builtin_setvar_multiple(peer, interfacevar);
05248       }
05249       
05250       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
05251       /* use  pbx_builtin_setvar to set a load of variables with one call */
05252       if (qe->parent->setqueueentryvar) {
05253          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
05254             (long) time(NULL) - qe->start, qe->opos);
05255          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
05256          pbx_builtin_setvar_multiple(peer, interfacevar);
05257       }
05258    
05259       ao2_unlock(qe->parent);
05260 
05261       /* try to set queue variables if configured to do so*/
05262       set_queue_variables(qe->parent, qe->chan);
05263       set_queue_variables(qe->parent, peer);
05264       
05265       ast_channel_lock(qe->chan);
05266       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
05267             monitorfilename = ast_strdupa(monitorfilename);
05268       }
05269       ast_channel_unlock(qe->chan);
05270       /* Begin Monitoring */
05271       if (qe->parent->monfmt && *qe->parent->monfmt) {
05272          if (!qe->parent->montype) {
05273             const char *monexec;
05274             ast_debug(1, "Starting Monitor as requested.\n");
05275             ast_channel_lock(qe->chan);
05276             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
05277                which = qe->chan;
05278                monexec = monexec ? ast_strdupa(monexec) : NULL;
05279             }
05280             else
05281                which = peer;
05282             ast_channel_unlock(qe->chan);
05283             if (monitorfilename) {
05284                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
05285             } else if (qe->chan->cdr) {
05286                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
05287             } else {
05288                /* Last ditch effort -- no CDR, make up something */
05289                snprintf(tmpid, sizeof(tmpid), "chan-%lx", (unsigned long)ast_random());
05290                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
05291             }
05292             if (!ast_strlen_zero(monexec)) {
05293                ast_monitor_setjoinfiles(which, 1);
05294             }
05295          } else {
05296             mixmonapp = pbx_findapp("MixMonitor");
05297             
05298             if (mixmonapp) {
05299                ast_debug(1, "Starting MixMonitor as requested.\n");
05300                if (!monitorfilename) {
05301                   if (qe->chan->cdr)
05302                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
05303                   else
05304                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", (unsigned long)ast_random());
05305                } else {
05306                   const char *m = monitorfilename;
05307                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
05308                      switch (*m) {
05309                      case '^':
05310                         if (*(m + 1) == '{')
05311                            *p = '$';
05312                         break;
05313                      case ',':
05314                         *p++ = '\\';
05315                         /* Fall through */
05316                      default:
05317                         *p = *m;
05318                      }
05319                      if (*m == '\0')
05320                         break;
05321                   }
05322                   if (p == tmpid2 + sizeof(tmpid2))
05323                      tmpid2[sizeof(tmpid2) - 1] = '\0';
05324 
05325                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
05326                }
05327 
05328                ast_channel_lock(qe->chan);
05329                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
05330                      monitor_exec = ast_strdupa(monitor_exec);
05331                }
05332                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
05333                      monitor_options = ast_strdupa(monitor_options);
05334                } else {
05335                   monitor_options = "";
05336                }
05337                ast_channel_unlock(qe->chan);
05338 
05339                if (monitor_exec) {
05340                   const char *m = monitor_exec;
05341                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
05342                      switch (*m) {
05343                      case '^':
05344                         if (*(m + 1) == '{')
05345                            *p = '$';
05346                         break;
05347                      case ',':
05348                         *p++ = '\\';
05349                         /* Fall through */
05350                      default:
05351                         *p = *m;
05352                      }
05353                      if (*m == '\0')
05354                         break;
05355                   }
05356                   if (p == meid2 + sizeof(meid2))
05357                      meid2[sizeof(meid2) - 1] = '\0';
05358 
05359                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
05360                }
05361    
05362                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
05363 
05364                if (!ast_strlen_zero(monitor_exec))
05365                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
05366                else
05367                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
05368                
05369                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
05370                /* We purposely lock the CDR so that pbx_exec does not update the application data */
05371                if (qe->chan->cdr)
05372                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
05373                pbx_exec(qe->chan, mixmonapp, mixmonargs);
05374                if (qe->chan->cdr)
05375                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
05376 
05377             } else {
05378                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
05379             }
05380          }
05381       }
05382       /* Drop out of the queue at this point, to prepare for next caller */
05383       leave_queue(qe);        
05384       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
05385          ast_debug(1, "app_queue: sendurl=%s.\n", url);
05386          ast_channel_sendurl(peer, url);
05387       }
05388       
05389       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
05390       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
05391       if (!ast_strlen_zero(macro)) {
05392             macroexec = ast_strdupa(macro);
05393       } else {
05394          if (qe->parent->membermacro)
05395             macroexec = ast_strdupa(qe->parent->membermacro);
05396       }
05397 
05398       if (!ast_strlen_zero(macroexec)) {
05399          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
05400          
05401          res = ast_autoservice_start(qe->chan);
05402          if (res) {
05403             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
05404             res = -1;
05405          }
05406          
05407          application = pbx_findapp("Macro");
05408 
05409          if (application) {
05410             res = pbx_exec(peer, application, macroexec);
05411             ast_debug(1, "Macro exited with status %d\n", res);
05412             res = 0;
05413          } else {
05414             ast_log(LOG_ERROR, "Could not find application Macro\n");
05415             res = -1;
05416          }
05417 
05418          if (ast_autoservice_stop(qe->chan) < 0) {
05419             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05420             res = -1;
05421          }
05422       }
05423 
05424       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
05425       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
05426       if (!ast_strlen_zero(gosub)) {
05427             gosubexec = ast_strdupa(gosub);
05428       } else {
05429          if (qe->parent->membergosub)
05430             gosubexec = ast_strdupa(qe->parent->membergosub);
05431       }
05432 
05433       if (!ast_strlen_zero(gosubexec)) {
05434          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
05435          
05436          res = ast_autoservice_start(qe->chan);
05437          if (res) {
05438             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
05439             res = -1;
05440          }
05441          
05442          application = pbx_findapp("Gosub");
05443          
05444          if (application) {
05445             char *gosub_args, *gosub_argstart;
05446 
05447             /* Set where we came from */
05448             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
05449             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
05450             peer->priority = 0;
05451 
05452             gosub_argstart = strchr(gosubexec, ',');
05453             if (gosub_argstart) {
05454                const char *what_is_s = "s";
05455                *gosub_argstart = 0;
05456                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05457                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05458                   what_is_s = "~~s~~";
05459                }
05460                if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
05461                   gosub_args = NULL;
05462                }
05463                *gosub_argstart = ',';
05464             } else {
05465                const char *what_is_s = "s";
05466                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05467                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05468                   what_is_s = "~~s~~";
05469                }
05470                if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
05471                   gosub_args = NULL;
05472                }
05473             }
05474             if (gosub_args) {
05475                res = pbx_exec(peer, application, gosub_args);
05476                if (!res) {
05477                   struct ast_pbx_args args;
05478                   memset(&args, 0, sizeof(args));
05479                   args.no_hangup_chan = 1;
05480                   ast_pbx_run_args(peer, &args);
05481                }
05482                ast_free(gosub_args);
05483                ast_debug(1, "Gosub exited with status %d\n", res);
05484             } else {
05485                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
05486             }
05487          } else {
05488             ast_log(LOG_ERROR, "Could not find application Gosub\n");
05489             res = -1;
05490          }
05491       
05492          if (ast_autoservice_stop(qe->chan) < 0) {
05493             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05494             res = -1;
05495          }
05496       }
05497 
05498       if (!ast_strlen_zero(agi)) {
05499          ast_debug(1, "app_queue: agi=%s.\n", agi);
05500          application = pbx_findapp("agi");
05501          if (application) {
05502             agiexec = ast_strdupa(agi);
05503             pbx_exec(qe->chan, application, agiexec);
05504          } else
05505             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
05506       }
05507       qe->handled++;
05508       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
05509                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
05510 
05511       if (qe->chan->cdr) {
05512          struct ast_cdr *cdr;
05513          struct ast_cdr *newcdr;
05514 
05515          /* Only work with the last CDR in the stack*/
05516          cdr = qe->chan->cdr;
05517          while (cdr->next) {
05518             cdr = cdr->next;
05519          }
05520 
05521          /* If this CDR is not related to us add new one*/
05522          if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) &&
05523              (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) &&
05524              (newcdr = ast_cdr_dup(cdr))) {
05525             ast_channel_lock(qe->chan);
05526             ast_cdr_init(newcdr, qe->chan);
05527             ast_cdr_reset(newcdr, 0);
05528             cdr = ast_cdr_append(cdr, newcdr);
05529             cdr = cdr->next;
05530             ast_channel_unlock(qe->chan);
05531          }
05532 
05533          if (update_cdr) {
05534             ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
05535          }
05536       }
05537 
05538       if (qe->parent->eventwhencalled)
05539          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
05540                "Queue: %s\r\n"
05541                "Uniqueid: %s\r\n"
05542                "Channel: %s\r\n"
05543                "Member: %s\r\n"
05544                "MemberName: %s\r\n"
05545                "Holdtime: %ld\r\n"
05546                "BridgedChannel: %s\r\n"
05547                "Ringtime: %ld\r\n"
05548                "%s",
05549                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
05550                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
05551                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05552       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
05553       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
05554    
05555       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
05556          queue_end_bridge->q = qe->parent;
05557          queue_end_bridge->chan = qe->chan;
05558          bridge_config.end_bridge_callback = end_bridge_callback;
05559          bridge_config.end_bridge_callback_data = queue_end_bridge;
05560          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
05561          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
05562           * to make sure to increase the refcount of this queue so it cannot be freed until we
05563           * are done with it. We remove this reference in end_bridge_callback.
05564           */
05565          queue_t_ref(qe->parent, "For bridge_config reference");
05566       }
05567 
05568       time(&callstart);
05569       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
05570       bridge = ast_bridge_call(qe->chan, peer, &bridge_config);
05571 
05572       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
05573        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
05574        * the AgentComplete manager event
05575        */
05576       ast_channel_lock(qe->chan);
05577       if (!attended_transfer_occurred(qe->chan)) {
05578          struct ast_datastore *tds;
05579 
05580          /* detect a blind transfer */
05581          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
05582             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
05583                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
05584                (long) (time(NULL) - callstart), qe->opos);
05585             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05586          } else if (ast_check_hangup(qe->chan) && !ast_check_hangup(peer)) {
05587             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
05588                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05589             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
05590          } else {
05591             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
05592                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05593             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
05594          }
05595          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
05596             ast_channel_datastore_remove(qe->chan, tds);
05597             /* tds was added by setup_transfer_datastore() and is freed below. */
05598          }
05599          ast_channel_unlock(qe->chan);
05600          update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
05601       } else {
05602          ast_channel_unlock(qe->chan);
05603 
05604          /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
05605          send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05606       }
05607 
05608       if (transfer_ds) {
05609          ast_datastore_free(transfer_ds);
05610       }
05611       ast_hangup(peer);
05612       res = bridge ? bridge : 1;
05613       ao2_ref(member, -1);
05614    }
05615 out:
05616    hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
05617 
05618    return res;
05619 }

static int unload_module ( void   )  [static]

Definition at line 9061 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ao2_t_iterator_next, ARRAY_LEN, ast_cli_unregister_multiple(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_data_unregister, ast_event_unsubscribe(), ast_extension_state_del(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), extension_state_cb(), queue_t_unref, queues, and queues_t_unlink.

09062 {
09063    int res;
09064    struct ast_context *con;
09065    struct ao2_iterator q_iter;
09066    struct call_queue *q = NULL;
09067 
09068    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
09069    res = ast_manager_unregister("QueueStatus");
09070    res |= ast_manager_unregister("Queues");
09071    res |= ast_manager_unregister("QueueRule");
09072    res |= ast_manager_unregister("QueueSummary");
09073    res |= ast_manager_unregister("QueueAdd");
09074    res |= ast_manager_unregister("QueueRemove");
09075    res |= ast_manager_unregister("QueuePause");
09076    res |= ast_manager_unregister("QueueLog");
09077    res |= ast_manager_unregister("QueuePenalty");
09078    res |= ast_manager_unregister("QueueReload");
09079    res |= ast_manager_unregister("QueueReset");
09080    res |= ast_unregister_application(app_aqm);
09081    res |= ast_unregister_application(app_rqm);
09082    res |= ast_unregister_application(app_pqm);
09083    res |= ast_unregister_application(app_upqm);
09084    res |= ast_unregister_application(app_ql);
09085    res |= ast_unregister_application(app);
09086    res |= ast_custom_function_unregister(&queueexists_function);
09087    res |= ast_custom_function_unregister(&queuevar_function);
09088    res |= ast_custom_function_unregister(&queuemembercount_function);
09089    res |= ast_custom_function_unregister(&queuemembercount_dep);
09090    res |= ast_custom_function_unregister(&queuememberlist_function);
09091    res |= ast_custom_function_unregister(&queuewaitingcount_function);
09092    res |= ast_custom_function_unregister(&queuememberpenalty_function);
09093    res |= ast_custom_function_unregister(&queuememberstatus_function);
09094    res |= ast_custom_function_unregister(&queuememberpaused_function);
09095 
09096    res |= ast_data_unregister(NULL);
09097 
09098    if (device_state_sub)
09099       ast_event_unsubscribe(device_state_sub);
09100 
09101    ast_extension_state_del(0, extension_state_cb);
09102 
09103    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
09104       ast_context_remove_extension2(con, "s", 1, NULL, 0);
09105       ast_context_destroy(con, "app_queue"); /* leave no trace */
09106    }
09107 
09108    q_iter = ao2_iterator_init(queues, 0);
09109    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
09110       queues_t_unlink(queues, q, "Remove queue from container due to unload");
09111       queue_t_unref(q, "Done with iterator");
09112    }
09113    ao2_iterator_destroy(&q_iter);
09114    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
09115    ao2_ref(queues, -1);
09116    ast_unload_realtime("queue_members");
09117    return res;
09118 }

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

References ast_debug, AST_LIST_NEXT, queue_ent::chan, queue_ent::max_penalty, queue_ent::min_penalty, and pbx_builtin_setvar_helper().

Referenced by queue_exec(), and wait_our_turn().

04364 {
04365    int max_penalty = INT_MAX;
04366 
04367    if (qe->max_penalty != INT_MAX) {
04368       char max_penalty_str[20];
04369 
04370       if (qe->pr->max_relative) {
04371          max_penalty = qe->max_penalty + qe->pr->max_value;
04372       } else {
04373          max_penalty = qe->pr->max_value;
04374       }
04375 
04376       /* a relative change to the penalty could put it below 0 */
04377       if (max_penalty < 0) {
04378          max_penalty = 0;
04379       }
04380 
04381       snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
04382       pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
04383       qe->max_penalty = max_penalty;
04384       ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
04385          qe->max_penalty, qe->chan->name, qe->pr->time);
04386    }
04387 
04388    if (qe->min_penalty != INT_MAX) {
04389       char min_penalty_str[20];
04390       int min_penalty;
04391 
04392       if (qe->pr->min_relative) {
04393          min_penalty = qe->min_penalty + qe->pr->min_value;
04394       } else {
04395          min_penalty = qe->pr->min_value;
04396       }
04397 
04398       if (min_penalty < 0) {
04399          min_penalty = 0;
04400       }
04401 
04402       if (max_penalty != INT_MAX && min_penalty > max_penalty) {
04403          min_penalty = max_penalty;
04404       }
04405 
04406       snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
04407       pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
04408       qe->min_penalty = min_penalty;
04409       ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
04410          qe->min_penalty, qe->chan->name, qe->pr->time);
04411    }
04412 
04413    qe->pr = AST_LIST_NEXT(qe->pr, list);
04414 }

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

update the queue status

Return values:
Always 0

Definition at line 4502 of file app_queue.c.

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

Referenced by queue_transfer_fixup(), and try_calling().

04503 {
04504    int oldtalktime;
04505 
04506    struct member *mem;
04507    struct call_queue *qtmp;
04508    struct ao2_iterator queue_iter;  
04509    
04510    if (shared_lastcall) {
04511       queue_iter = ao2_iterator_init(queues, 0);
04512       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04513          ao2_lock(qtmp);
04514          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04515             time(&mem->lastcall);
04516             mem->calls++;
04517             mem->lastqueue = q;
04518             ao2_ref(mem, -1);
04519          }
04520          ao2_unlock(qtmp);
04521          queue_t_unref(qtmp, "Done with iterator");
04522       }
04523       ao2_iterator_destroy(&queue_iter);
04524    } else {
04525       ao2_lock(q);
04526       time(&member->lastcall);
04527       member->calls++;
04528       member->lastqueue = q;
04529       ao2_unlock(q);
04530    }  
04531    ao2_lock(q);
04532    q->callscompleted++;
04533    if (callcompletedinsl)
04534       q->callscompletedinsl++;
04535    /* Calculate talktime using the same exponential average as holdtime code*/
04536    oldtalktime = q->talktime;
04537    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04538    ao2_unlock(q);
04539    return 0;
04540 }

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

Definition at line 2624 of file app_queue.c.

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

Referenced by set_member_paused().

02625 {
02626    int ret = -1;
02627 
02628    if (ast_strlen_zero(mem->rt_uniqueid))
02629       return ret;
02630 
02631    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02632       ret = 0;
02633 
02634    return ret;
02635 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 2638 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, member_remove_from_queue(), call_queue::members, member::realtime, rt_handle_member_record(), S_OR, and SENTINEL.

Referenced by load_realtime_queue(), and queue_exec().

02639 {
02640    struct ast_config *member_config = NULL;
02641    struct member *m;
02642    char *interface = NULL;
02643    struct ao2_iterator mem_iter;
02644 
02645    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02646       /* This queue doesn't have realtime members. If the queue still has any realtime
02647        * members in memory, they need to be removed.
02648        */
02649       ao2_lock(q);
02650       mem_iter = ao2_iterator_init(q->members, 0);
02651       while ((m = ao2_iterator_next(&mem_iter))) {
02652          if (m->realtime) {
02653             member_remove_from_queue(q, m);
02654          }
02655          ao2_ref(m, -1);
02656       }
02657       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02658       ao2_unlock(q);
02659       return;
02660    }
02661 
02662    ao2_lock(q);
02663 
02664    /* Temporarily set realtime  members dead so we can detect deleted ones.*/
02665    mem_iter = ao2_iterator_init(q->members, 0);
02666    while ((m = ao2_iterator_next(&mem_iter))) {
02667       if (m->realtime)
02668          m->dead = 1;
02669       ao2_ref(m, -1);
02670    }
02671    ao2_iterator_destroy(&mem_iter);
02672 
02673    while ((interface = ast_category_browse(member_config, interface))) {
02674       rt_handle_member_record(q, interface,
02675          ast_variable_retrieve(member_config, interface, "uniqueid"),
02676          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02677          ast_variable_retrieve(member_config, interface, "penalty"),
02678          ast_variable_retrieve(member_config, interface, "paused"),
02679          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02680    }
02681 
02682    /* Delete all realtime members that have been deleted in DB. */
02683    mem_iter = ao2_iterator_init(q->members, 0);
02684    while ((m = ao2_iterator_next(&mem_iter))) {
02685       if (m->dead) {
02686          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02687          member_remove_from_queue(q, m);
02688       }
02689       ao2_ref(m, -1);
02690    }
02691    ao2_iterator_destroy(&mem_iter);
02692    ao2_unlock(q);
02693    ast_config_destroy(member_config);
02694 }

static int update_status ( struct call_queue q,
struct member m,
const int  status 
) [static]

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

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

Definition at line 1626 of file app_queue.c.

References member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event, call_queue::maskmemberstatus, member::membername, member::paused, member::penalty, member::realtime, and member::status.

Referenced by extension_state_cb(), and handle_statechange().

01627 {
01628    m->status = status;
01629 
01630    if (q->maskmemberstatus)
01631       return 0;
01632 
01633    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01634       "Queue: %s\r\n"
01635       "Location: %s\r\n"
01636       "MemberName: %s\r\n"
01637       "Membership: %s\r\n"
01638       "Penalty: %d\r\n"
01639       "CallsTaken: %d\r\n"
01640       "LastCall: %d\r\n"
01641       "Status: %d\r\n"
01642       "Paused: %d\r\n",
01643       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01644       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01645    );
01646 
01647    return 0;
01648 }

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

UnPauseQueueMember application.

Definition at line 6099 of file app_queue.c.

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

Referenced by load_module().

06100 {
06101    char *parse;
06102    AST_DECLARE_APP_ARGS(args,
06103       AST_APP_ARG(queuename);
06104       AST_APP_ARG(interface);
06105       AST_APP_ARG(options);
06106       AST_APP_ARG(reason);
06107    );
06108 
06109    if (ast_strlen_zero(data)) {
06110       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
06111       return -1;
06112    }
06113 
06114    parse = ast_strdupa(data);
06115 
06116    AST_STANDARD_APP_ARGS(args, parse);
06117 
06118    if (ast_strlen_zero(args.interface)) {
06119       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
06120       return -1;
06121    }
06122 
06123    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
06124       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
06125       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
06126       return 0;
06127    }
06128 
06129    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
06130 
06131    return 0;
06132 }

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

Check for valid exit from queue via goto.

Return values:
0 if failure
1 if successful

Definition at line 2811 of file app_queue.c.

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

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

02812 {
02813    int digitlen = strlen(qe->digits);
02814 
02815    /* Prevent possible buffer overflow */
02816    if (digitlen < sizeof(qe->digits) - 2) {
02817       qe->digits[digitlen] = digit;
02818       qe->digits[digitlen + 1] = '\0';
02819    } else {
02820       qe->digits[0] = '\0';
02821       return 0;
02822    }
02823 
02824    /* If there's no context to goto, short-circuit */
02825    if (ast_strlen_zero(qe->context))
02826       return 0;
02827 
02828    /* If the extension is bad, then reset the digits to blank */
02829    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02830       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02831       qe->digits[0] = '\0';
02832       return 0;
02833    }
02834 
02835    /* We have an exact match */
02836    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02837       qe->valid_digits = 1;
02838       /* Return 1 on a successful goto */
02839       return 1;
02840    }
02841 
02842    return 0;
02843 }

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

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

Definition at line 3202 of file app_queue.c.

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

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

03203 {
03204    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
03205    const char *tmp;
03206 
03207    if (pbx_builtin_serialize_variables(chan, &buf)) {
03208       int i, j;
03209 
03210       /* convert "\n" to "\nVariable: " */
03211       strcpy(vars, "Variable: ");
03212       tmp = ast_str_buffer(buf);
03213 
03214       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
03215          vars[j] = tmp[i];
03216 
03217          if (tmp[i + 1] == '\0')
03218             break;
03219          if (tmp[i] == '\n') {
03220             vars[j++] = '\r';
03221             vars[j++] = '\n';
03222 
03223             ast_copy_string(&(vars[j]), "Variable: ", len - j);
03224             j += 9;
03225          }
03226       }
03227       if (j > len - 3)
03228          j = len - 3;
03229       vars[j++] = '\r';
03230       vars[j++] = '\n';
03231       vars[j] = '\0';
03232    } else {
03233       /* there are no channel variables; leave it blank */
03234       *vars = '\0';
03235    }
03236    return vars;
03237 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5621 of file app_queue.c.

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

Referenced by queue_exec().

05622 {
05623    /* Don't need to hold the lock while we setup the outgoing calls */
05624    int retrywait = qe->parent->retry * 1000;
05625 
05626    int res = ast_waitfordigit(qe->chan, retrywait);
05627    if (res > 0 && !valid_exit(qe, res))
05628       res = 0;
05629 
05630    return res;
05631 }

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

Wait for a member to answer the call.

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

Definition at line 3785 of file app_queue.c.

References ast_channel::_state, accountcode, call_queue::announce_to_first_user, call_queue::announcefrequency, callattempt::aoc_s_rate_list, ast_aoc_decode(), ast_aoc_destroy_decoded(), ast_aoc_destroy_encoded(), ast_aoc_encode(), ast_aoc_get_msg_type(), AST_AOC_S, ast_call(), ast_cdr_failed(), ast_cdr_noanswer(), AST_CEL_FORWARD, ast_cel_report_event(), ast_channel_connected_line_macro(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, AST_CHANNEL_NAME, ast_channel_redirecting_macro(), ast_channel_unlock, ast_channel_update_connected_line(), ast_channel_update_redirecting(), ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_connected_line_parse_data(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_WATCHERS, ast_moh_stop(), ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_party_connected_line_set(), ast_party_connected_line_set_init(), ast_party_number_free(), ast_party_number_init(), ast_party_redirecting_copy(), ast_party_redirecting_free(), ast_party_redirecting_init(), ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), ast_remaining_ms(), ast_request(), AST_STATE_UP, ast_strdup, ast_strdupa, ast_string_field_set, ast_strlen_zero(), ast_tvnow(), ast_verb, ast_waitfor_n(), callattempt::block_connected_update, callattempt::call_next, ast_channel::caller, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::connected, callattempt::connected, ast_channel::context, ast_frame::data, ast_frame::datalen, callattempt::dial_callerid_absent, ast_channel::dialed, do_hang(), ast_channel::exten, f, ast_frame::frametype, ast_party_redirecting::from, ast_channel::hangupcause, ast_party_caller::id, ast_frame_subclass::integer, callattempt::interface, member::interface, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, ast_channel::nativeformats, ast_party_id::number, queue_ent::parent, callattempt::pending_connected_update, call_queue::periodicannouncefrequency, queue_ent::pos, ast_frame::ptr, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ast_channel::redirecting, ring_one(), queue_ent::ring_when_ringing, rna(), S_OR, say_periodic_announcement(), say_position(), ast_party_connected_line::source, queue_ent::start, status, callattempt::stillgoing, ast_party_number::str, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, ast_party_dialed::transit_network_select, ast_frame::uint32, ast_party_number::valid, and valid_exit().

Referenced by try_calling().

03786 {
03787    const char *queue = qe->parent->name;
03788    struct callattempt *o, *start = NULL, *prev = NULL;
03789    int status;
03790    int numbusies = prebusies;
03791    int numnochan = 0;
03792    int stillgoing = 0;
03793    int orig = *to;
03794    struct ast_frame *f;
03795    struct callattempt *peer = NULL;
03796    struct ast_channel *winner;
03797    struct ast_channel *in = qe->chan;
03798    char on[80] = "";
03799    char membername[80] = "";
03800    long starttime = 0;
03801    long endtime = 0;
03802 #ifdef HAVE_EPOLL
03803    struct callattempt *epollo;
03804 #endif
03805    struct ast_party_connected_line connected_caller;
03806    char *inchan_name;
03807    struct timeval start_time_tv = ast_tvnow();
03808 
03809    ast_party_connected_line_init(&connected_caller);
03810 
03811    ast_channel_lock(qe->chan);
03812    inchan_name = ast_strdupa(qe->chan->name);
03813    ast_channel_unlock(qe->chan);
03814 
03815    starttime = (long) time(NULL);
03816 #ifdef HAVE_EPOLL
03817    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03818       if (epollo->chan)
03819          ast_poll_channel_add(in, epollo->chan);
03820    }
03821 #endif
03822 
03823    while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
03824       int numlines, retry, pos = 1;
03825       struct ast_channel *watchers[AST_MAX_WATCHERS];
03826       watchers[0] = in;
03827       start = NULL;
03828 
03829       for (retry = 0; retry < 2; retry++) {
03830          numlines = 0;
03831          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
03832             if (o->stillgoing) { /* Keep track of important channels */
03833                stillgoing = 1;
03834                if (o->chan) {
03835                   if (pos < AST_MAX_WATCHERS) {
03836                      watchers[pos++] = o->chan;
03837                   }
03838                   if (!start)
03839                      start = o;
03840                   else
03841                      prev->call_next = o;
03842                   prev = o;
03843                }
03844             }
03845             numlines++;
03846          }
03847          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
03848             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
03849             break;
03850          /* On "ringall" strategy we only move to the next penalty level
03851             when *all* ringing phones are done in the current penalty level */
03852          ring_one(qe, outgoing, &numbusies);
03853          /* and retry... */
03854       }
03855       if (pos == 1 /* not found */) {
03856          if (numlines == (numbusies + numnochan)) {
03857             ast_debug(1, "Everyone is busy at this time\n");
03858             //if (in->cdr && in->_state != AST_STATE_UP) {
03859             // ast_cdr_busy(in->cdr);
03860             //}
03861          } else {
03862             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03863             if (in->cdr && in->_state != AST_STATE_UP) {
03864                ast_cdr_failed(in->cdr);
03865             }
03866          }
03867          *to = 0;
03868          return NULL;
03869       }
03870 
03871       /* Poll for events from both the incoming channel as well as any outgoing channels */
03872       winner = ast_waitfor_n(watchers, pos, to);
03873 
03874       /* Service all of the outgoing channels */
03875       for (o = start; o; o = o->call_next) {
03876          /* We go with a fixed buffer here instead of using ast_strdupa. Using
03877           * ast_strdupa in a loop like this one can cause a stack overflow
03878           */
03879          char ochan_name[AST_CHANNEL_NAME];
03880 
03881          if (o->chan) {
03882             ast_channel_lock(o->chan);
03883             ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name));
03884             ast_channel_unlock(o->chan);
03885          }
03886          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
03887             if (!peer) {
03888                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03889                if (!o->block_connected_update) {
03890                   if (o->pending_connected_update) {
03891                      if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03892                         ast_channel_update_connected_line(in, &o->connected, NULL);
03893                      }
03894                   } else if (!o->dial_callerid_absent) {
03895                      ast_channel_lock(o->chan);
03896                      ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03897                      ast_channel_unlock(o->chan);
03898                      connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03899                      if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03900                         ast_channel_update_connected_line(in, &connected_caller, NULL);
03901                      }
03902                      ast_party_connected_line_free(&connected_caller);
03903                   }
03904                }
03905                if (o->aoc_s_rate_list) {
03906                   size_t encoded_size;
03907                   struct ast_aoc_encoded *encoded;
03908                   if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03909                      ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03910                      ast_aoc_destroy_encoded(encoded);
03911                   }
03912                }
03913                peer = o;
03914             }
03915          } else if (o->chan && (o->chan == winner)) {
03916 
03917             ast_copy_string(on, o->member->interface, sizeof(on));
03918             ast_copy_string(membername, o->member->membername, sizeof(membername));
03919 
03920             /* Before processing channel, go ahead and check for forwarding */
03921             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03922                ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03923                numnochan++;
03924                do_hang(o);
03925                winner = NULL;
03926                continue;
03927             } else if (!ast_strlen_zero(o->chan->call_forward)) {
03928                struct ast_channel *original = o->chan;
03929                char tmpchan[256];
03930                char *stuff;
03931                char *tech;
03932 
03933                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03934                if ((stuff = strchr(tmpchan, '/'))) {
03935                   *stuff++ = '\0';
03936                   tech = tmpchan;
03937                } else {
03938                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03939                   stuff = tmpchan;
03940                   tech = "Local";
03941                }
03942                if (!strcasecmp(tech, "Local")) {
03943                   /*
03944                    * Drop the connected line update block for local channels since
03945                    * this is going to run dialplan and the user can change his
03946                    * mind about what connected line information he wants to send.
03947                    */
03948                   o->block_connected_update = 0;
03949                }
03950 
03951                ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03952 
03953                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03954                /* Setup parameters */
03955                o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03956                if (!o->chan) {
03957                   ast_log(LOG_NOTICE,
03958                      "Forwarding failed to create channel to dial '%s/%s'\n",
03959                      tech, stuff);
03960                   o->stillgoing = 0;
03961                   numnochan++;
03962                } else {
03963                   ast_channel_lock_both(o->chan, original);
03964                   ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting);
03965                   ast_channel_unlock(o->chan);
03966                   ast_channel_unlock(original);
03967 
03968                   ast_channel_lock_both(o->chan, in);
03969                   ast_channel_inherit_variables(in, o->chan);
03970                   ast_channel_datastore_inherit(in, o->chan);
03971 
03972                   if (o->pending_connected_update) {
03973                      /*
03974                       * Re-seed the callattempt's connected line information with
03975                       * previously acquired connected line info from the queued
03976                       * channel.  The previously acquired connected line info could
03977                       * have been set through the CONNECTED_LINE dialplan function.
03978                       */
03979                      o->pending_connected_update = 0;
03980                      ast_party_connected_line_copy(&o->connected, &in->connected);
03981                   }
03982 
03983                   ast_string_field_set(o->chan, accountcode, in->accountcode);
03984 
03985                   if (!o->chan->redirecting.from.number.valid
03986                      || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03987                      /*
03988                       * The call was not previously redirected so it is
03989                       * now redirected from this number.
03990                       */
03991                      ast_party_number_free(&o->chan->redirecting.from.number);
03992                      ast_party_number_init(&o->chan->redirecting.from.number);
03993                      o->chan->redirecting.from.number.valid = 1;
03994                      o->chan->redirecting.from.number.str =
03995                         ast_strdup(S_OR(in->macroexten, in->exten));
03996                   }
03997 
03998                   o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03999 
04000                   o->dial_callerid_absent = !o->chan->caller.id.number.valid
04001                      || ast_strlen_zero(o->chan->caller.id.number.str);
04002                   ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller);
04003 
04004                   ast_channel_unlock(in);
04005                   if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
04006                      && !o->block_connected_update) {
04007                      struct ast_party_redirecting redirecting;
04008 
04009                      /*
04010                       * Redirecting updates to the caller make sense only on single
04011                       * call at a time strategies.
04012                       *
04013                       * We must unlock o->chan before calling
04014                       * ast_channel_redirecting_macro, because we put o->chan into
04015                       * autoservice there.  That is pretty much a guaranteed
04016                       * deadlock.  This is why the handling of o->chan's lock may
04017                       * seem a bit unusual here.
04018                       */
04019                      ast_party_redirecting_init(&redirecting);
04020                      ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
04021                      ast_channel_unlock(o->chan);
04022                      if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
04023                         ast_channel_update_redirecting(in, &redirecting, NULL);
04024                      }
04025                      ast_party_redirecting_free(&redirecting);
04026                   } else {
04027                      ast_channel_unlock(o->chan);
04028                   }
04029 
04030                   if (ast_call(o->chan, stuff, 0)) {
04031                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
04032                         tech, stuff);
04033                      do_hang(o);
04034                      numnochan++;
04035                   }
04036                }
04037                /* Hangup the original channel now, in case we needed it */
04038                ast_hangup(winner);
04039                continue;
04040             }
04041             f = ast_read(winner);
04042             if (f) {
04043                if (f->frametype == AST_FRAME_CONTROL) {
04044                   switch (f->subclass.integer) {
04045                   case AST_CONTROL_ANSWER:
04046                      /* This is our guy if someone answered. */
04047                      if (!peer) {
04048                         ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
04049                         if (!o->block_connected_update) {
04050                            if (o->pending_connected_update) {
04051                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
04052                                  ast_channel_update_connected_line(in, &o->connected, NULL);
04053                               }
04054                            } else if (!o->dial_callerid_absent) {
04055                               ast_channel_lock(o->chan);
04056                               ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
04057                               ast_channel_unlock(o->chan);
04058                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
04059                               if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
04060                                  ast_channel_update_connected_line(in, &connected_caller, NULL);
04061                               }
04062                               ast_party_connected_line_free(&connected_caller);
04063                            }
04064                         }
04065                         if (o->aoc_s_rate_list) {
04066                            size_t encoded_size;
04067                            struct ast_aoc_encoded *encoded;
04068                            if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
04069                               ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
04070                               ast_aoc_destroy_encoded(encoded);
04071                            }
04072                         }
04073                         peer = o;
04074                      }
04075                      break;
04076                   case AST_CONTROL_BUSY:
04077                      ast_verb(3, "%s is busy\n", ochan_name);
04078                      //if (in->cdr)
04079                      //  ast_cdr_busy(in->cdr);
04080                      do_hang(o);
04081                      endtime = (long) time(NULL);
04082                      endtime -= starttime;
04083                      rna(endtime * 1000, qe, on, membername, 0);
04084                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
04085                         if (qe->parent->timeoutrestart) {
04086                            start_time_tv = ast_tvnow();
04087                         }
04088                         /* Have enough time for a queue member to answer? */
04089                         if (ast_remaining_ms(start_time_tv, orig) > 500) {
04090                            ring_one(qe, outgoing, &numbusies);
04091                            starttime = (long) time(NULL);
04092                         }
04093                      }
04094                      numbusies++;
04095                      break;
04096                   case AST_CONTROL_CONGESTION:
04097                      ast_verb(3, "%s is circuit-busy\n", ochan_name);
04098                      if (in->cdr)
04099                         ast_cdr_failed(in->cdr);
04100                      endtime = (long) time(NULL);
04101                      endtime -= starttime;
04102                      rna(endtime * 1000, qe, on, membername, 0);
04103                      do_hang(o);
04104                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
04105                         if (qe->parent->timeoutrestart) {
04106                            start_time_tv = ast_tvnow();
04107                         }
04108                         if (ast_remaining_ms(start_time_tv, orig) > 500) {
04109                            ring_one(qe, outgoing, &numbusies);
04110                            starttime = (long) time(NULL);
04111                         }
04112                      }
04113                      numbusies++;
04114                      break;
04115                   case AST_CONTROL_RINGING:
04116                      ast_verb(3, "%s is ringing\n", ochan_name);
04117 
04118                      /* Start ring indication when the channel is ringing, if specified */
04119                      if (qe->ring_when_ringing) {
04120                         ast_moh_stop(qe->chan);
04121                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
04122                      }
04123                      break;
04124                   case AST_CONTROL_OFFHOOK:
04125                      /* Ignore going off hook */
04126                      break;
04127                   case AST_CONTROL_CONNECTED_LINE:
04128                      if (o->block_connected_update) {
04129                         ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
04130                         break;
04131                      }
04132                      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
04133                         struct ast_party_connected_line connected;
04134 
04135                         ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
04136                         ast_party_connected_line_set_init(&connected, &o->connected);
04137                         ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
04138                         ast_party_connected_line_set(&o->connected, &connected, NULL);
04139                         ast_party_connected_line_free(&connected);
04140                         o->pending_connected_update = 1;
04141                         break;
04142                      }
04143 
04144                      /*
04145                       * Prevent using the CallerID from the outgoing channel since we
04146                       * got a connected line update from it.
04147                       */
04148                      o->dial_callerid_absent = 1;
04149 
04150                      if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
04151                         ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
04152                      }
04153                      break;
04154                   case AST_CONTROL_AOC:
04155                      {
04156                         struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
04157                         if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
04158                            ast_aoc_destroy_decoded(o->aoc_s_rate_list);
04159                            o->aoc_s_rate_list = decoded;
04160                         } else {
04161                            ast_aoc_destroy_decoded(decoded);
04162                         }
04163                      }
04164                      break;
04165                   case AST_CONTROL_REDIRECTING:
04166                      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
04167                         /*
04168                          * Redirecting updates to the caller make sense only on single
04169                          * call at a time strategies.
04170                          */
04171                         break;
04172                      }
04173                      if (o->block_connected_update) {
04174                         ast_verb(3, "Redirecting update to %s prevented\n",
04175                            inchan_name);
04176                         break;
04177                      }
04178                      ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
04179                         ochan_name, inchan_name);
04180                      if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
04181                         ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
04182                      }
04183                      break;
04184                   default:
04185                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
04186                      break;
04187                   }
04188                }
04189                ast_frfree(f);
04190             } else { /* ast_read() returned NULL */
04191                endtime = (long) time(NULL) - starttime;
04192                rna(endtime * 1000, qe, on, membername, 1);
04193                do_hang(o);
04194                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
04195                   if (qe->parent->timeoutrestart) {
04196                      start_time_tv = ast_tvnow();
04197                   }
04198                   if (ast_remaining_ms(start_time_tv, orig) > 500) {
04199                      ring_one(qe, outgoing, &numbusies);
04200                      starttime = (long) time(NULL);
04201                   }
04202                }
04203             }
04204          }
04205       }
04206 
04207       /* If we received an event from the caller, deal with it. */
04208       if (winner == in) {
04209          f = ast_read(in);
04210          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
04211             /* Got hung up */
04212             *to = -1;
04213             if (f) {
04214                if (f->data.uint32) {
04215                   in->hangupcause = f->data.uint32;
04216                }
04217                ast_frfree(f);
04218             }
04219             return NULL;
04220          }
04221 
04222          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
04223             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
04224             *to = 0;
04225             ast_frfree(f);
04226             if (in->cdr && in->_state != AST_STATE_UP) {
04227                ast_cdr_noanswer(in->cdr);
04228             }
04229             return NULL;
04230          }
04231          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
04232             ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
04233             *to = 0;
04234             *digit = f->subclass.integer;
04235             ast_frfree(f);
04236             if (in->cdr && in->_state != AST_STATE_UP) {
04237                ast_cdr_noanswer(in->cdr);
04238             }
04239             return NULL;
04240          }
04241 
04242          /* Send the frame from the in channel to all outgoing channels. */
04243          for (o = start; o; o = o->call_next) {
04244             if (!o->stillgoing || !o->chan) {
04245                /* This outgoing channel has died so don't send the frame to it. */
04246                continue;
04247             }
04248             switch (f->frametype) {
04249             case AST_FRAME_CONTROL:
04250                switch (f->subclass.integer) {
04251                case AST_CONTROL_CONNECTED_LINE:
04252                   if (ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
04253                      ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
04254                   }
04255                   break;
04256                case AST_CONTROL_REDIRECTING:
04257                   if (ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
04258                      ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
04259                   }
04260                   break;
04261                default:
04262                   /* We are not going to do anything with this frame. */
04263                   goto skip_frame;
04264                }
04265                break;
04266             default:
04267                /* We are not going to do anything with this frame. */
04268                goto skip_frame;
04269             }
04270          }
04271 skip_frame:;
04272 
04273          ast_frfree(f);
04274       }
04275    }
04276 
04277    /* Make a position announcement, if enabled */
04278    if (qe->parent->announcefrequency && qe->parent->announce_to_first_user) {
04279       say_position(qe, ringing);
04280    }
04281 
04282    /* Make a periodic announcement, if enabled */
04283    if (qe->parent->periodicannouncefrequency && qe->parent->announce_to_first_user) {
04284       say_periodic_announcement(qe, ringing);
04285    }
04286  
04287    if (!*to) {
04288       for (o = start; o; o = o->call_next) {
04289          rna(orig, qe, o->interface, o->member->membername, 1);
04290       }
04291    }
04292 
04293    if (in->cdr
04294        && in->_state != AST_STATE_UP
04295        && (!*to || ast_check_hangup(in))) {
04296      ast_cdr_noanswer(in->cdr);
04297    }
04298 
04299 #ifdef HAVE_EPOLL
04300    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
04301       if (epollo->chan)
04302          ast_poll_channel_del(in, epollo->chan);
04303    }
04304 #endif
04305 
04306    return peer;
04307 }

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

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

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

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

Definition at line 4426 of file app_queue.c.

References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, queue_ent::expire, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, queue_ent::min_penalty, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, update_qe_rule(), and valid_exit().

Referenced by queue_exec().

04427 {
04428    int res = 0;
04429 
04430    /* This is the holding pen for callers 2 through maxlen */
04431    for (;;) {
04432 
04433       if (is_our_turn(qe))
04434          break;
04435 
04436       /* If we have timed out, break out */
04437       if (qe->expire && (time(NULL) >= qe->expire)) {
04438          *reason = QUEUE_TIMEOUT;
04439          break;
04440       }
04441 
04442       if (qe->parent->leavewhenempty) {
04443          int status = 0;
04444 
04445          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty, 0))) {
04446             *reason = QUEUE_LEAVEEMPTY;
04447             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04448             leave_queue(qe);
04449             break;
04450          }
04451       }
04452 
04453       /* Make a position announcement, if enabled */
04454       if (qe->parent->announcefrequency &&
04455          (res = say_position(qe,ringing)))
04456          break;
04457 
04458       /* If we have timed out, break out */
04459       if (qe->expire && (time(NULL) >= qe->expire)) {
04460          *reason = QUEUE_TIMEOUT;
04461          break;
04462       }
04463 
04464       /* Make a periodic announcement, if enabled */
04465       if (qe->parent->periodicannouncefrequency &&
04466          (res = say_periodic_announcement(qe,ringing)))
04467          break;
04468       
04469       /* see if we need to move to the next penalty level for this queue */
04470       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04471          update_qe_rule(qe);
04472       }
04473 
04474       /* If we have timed out, break out */
04475       if (qe->expire && (time(NULL) >= qe->expire)) {
04476          *reason = QUEUE_TIMEOUT;
04477          break;
04478       }
04479       
04480       /* Wait a second before checking again */
04481       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04482          if (res > 0 && !valid_exit(qe, res))
04483             res = 0;
04484          else
04485             break;
04486       }
04487       
04488       /* If we have timed out, break out */
04489       if (qe->expire && (time(NULL) >= qe->expire)) {
04490          *reason = QUEUE_TIMEOUT;
04491          break;
04492       }
04493    }
04494 
04495    return res;
04496 }

static int word_in_list ( const char *  list,
const char *  word 
) [static]

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

Parameters:
list Space delimited list of words
word The 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 , not a space) since it is used for checking tab-completion and a word at the end is still being tab-completed.
Returns:
Returns 1 if the word is found
Returns 0 if the word is not found

Definition at line 7734 of file app_queue.c.

Referenced by complete_queue().

07734                                                             {
07735    int list_len, word_len = strlen(word);
07736    const char *find, *end_find, *end_list;
07737 
07738    /* strip whitespace from front */
07739    while (isspace(*list)) {
07740       list++;
07741    }
07742 
07743    while ((find = strstr(list, word))) {
07744       /* beginning of find starts inside another word? */
07745       if (find != list && *(find - 1) != ' ') {
07746          list = find;
07747          /* strip word from front */
07748          while (!isspace(*list) && *list != '\0') {
07749             list++;
07750          }
07751          /* strip whitespace from front */
07752          while (isspace(*list)) {
07753             list++;
07754          }
07755          continue;
07756       }
07757 
07758       /* end of find ends inside another word or at very end of list? */
07759       list_len = strlen(list);
07760       end_find = find + word_len;
07761       end_list = list + list_len;
07762       if (end_find == end_list || *end_find != ' ') {
07763          list = find;
07764          /* strip word from front */
07765          while (!isspace(*list) && *list != '\0') {
07766             list++;
07767          }
07768          /* strip whitespace from front */
07769          while (isspace(*list)) {
07770             list++;
07771          }
07772          continue;
07773       }
07774 
07775       /* terminating conditions satisfied, word at beginning or separated by ' ' */
07776       return 1;
07777    }
07778    
07779    return 0;
07780 }


Variable Documentation

char* app = "Queue" [static]

Definition at line 1035 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 1037 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 1041 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 1045 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 1039 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 1043 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 1060 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

Definition at line 8779 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 1069 of file app_queue.c.

Definition at line 1019 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 1063 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 1048 of file app_queue.c.

const char qpm_cmd_usage[] [static]
Initial value:
 
"Usage: queue pause member <channel> in <queue> reason <reason>\n"

Definition at line 8770 of file app_queue.c.

const char qsmp_cmd_usage[] [static]
Initial value:
"Usage: queue set member penalty <channel> from <queue> <penalty>\n"

Definition at line 8776 of file app_queue.c.

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

Definition at line 9057 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 1051 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 1054 of file app_queue.c.

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

Referenced by set_queue_result().

Initial value:
 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}

a datastore used to help correctly log attended transfers of queue callers

Definition at line 4684 of file app_queue.c.

Initial value:
 {
   .name = "QUEUE_EXISTS",
   .read = queue_function_exists,
}

Definition at line 7078 of file app_queue.c.

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

Definition at line 7093 of file app_queue.c.

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

Definition at line 7088 of file app_queue.c.

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

Definition at line 7103 of file app_queue.c.

Initial value:
 {
   .name = "QUEUE_MEMBER_PAUSED",
   .read = queue_function_queuememberpaused,
}

Definition at line 7119 of file app_queue.c.

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

Definition at line 7108 of file app_queue.c.

Initial value:
 {
   .name = "QUEUE_MEMBER_STATUS",
   .read = queue_function_queuememberstatus,
}

Definition at line 7114 of file app_queue.c.

struct ao2_container* queues [static]
Initial value:

Definition at line 9052 of file app_queue.c.

Initial value:
 {
   .name = "QUEUE_VARIABLES",
   .read = queue_function_var,
}

Definition at line 7083 of file app_queue.c.

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

Definition at line 7098 of file app_queue.c.

const char qum_cmd_usage[] [static]
Initial value:
"Usage: queue unpause member <channel> in <queue> reason <reason>\n"

Definition at line 8773 of file app_queue.c.

int shared_lastcall = 1 [static]

queues.conf [general] option

Definition at line 1066 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* text
int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 1072 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 1057 of file app_queue.c.


Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1