Thu Apr 3 08:20:46 2014

Asterisk developer's documentation


app_queue.c File Reference

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

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

Go to the source code of this file.

Data Structures

struct  autopause
struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  member
struct  penalty_rule
struct  queue_end_bridge
struct  queue_ent
struct  queue_transfer_ds
struct  rule_list
struct  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)
 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_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_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queueexists_function
static struct ast_custom_function queuemembercount_dep
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpenalty_function
static struct ao2_containerqueues
static struct ast_data_handler queues_data_provider
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 1
 queues.conf [general] option
static struct strategy strategies []
static int update_cdr = 0
 queues.conf [general] option
static int use_weight = 0
 queues.conf per-queue weight option

Detailed Description

True call queues with optional send URL on answer.

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

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

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
These features added by David C. Troy <dave@toad.net>:
  • 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 1065 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 1066 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_LIMIT   4

We not announce position more than <limit>

Definition at line 1081 of file app_queue.c.

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

#define ANNOUNCEPOSITION_MORE_THAN   3

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

Definition at line 1080 of file app_queue.c.

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

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 1079 of file app_queue.c.

Referenced by queue_set_param(), and queues_data_provider_get_helper().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 1078 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

Definition at line 3600 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 8455 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8520 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

Definition at line 8534 of file app_queue.c.

#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15

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

Definition at line 891 of file app_queue.c.

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 887 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 888 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

The maximum periodic announcements we can have

Definition at line 890 of file app_queue.c.

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 893 of file app_queue.c.

Referenced by load_module().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 1067 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 1328 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 1330 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 1331 of file app_queue.c.

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

#define RECHECK   1

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

Definition at line 889 of file app_queue.c.

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

anonymous enum
Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 847 of file app_queue.c.

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

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4431 of file app_queue.c.

04431                            {
04432    CALLER,
04433    AGENT,
04434    TRANSFER
04435 };

Enumerator:
QUEUE_EMPTY_PENALTY 
QUEUE_EMPTY_PAUSED 
QUEUE_EMPTY_INUSE 
QUEUE_EMPTY_RINGING 
QUEUE_EMPTY_UNAVAILABLE 
QUEUE_EMPTY_INVALID 
QUEUE_EMPTY_UNKNOWN 
QUEUE_EMPTY_WRAPUP 

Definition at line 1053 of file app_queue.c.

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

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 853 of file app_queue.c.

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

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 937 of file app_queue.c.

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

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 962 of file app_queue.c.

00962                             {
00963    TIMEOUT_PRIORITY_APP,
00964    TIMEOUT_PRIORITY_CONF,
00965 };


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

07255 {
07256    struct call_queue *q;
07257    struct ast_str *out = ast_str_alloca(240);
07258    int found = 0;
07259    time_t now = time(NULL);
07260    struct ao2_iterator queue_iter;
07261    struct ao2_iterator mem_iter;
07262 
07263    if (argc != 2 && argc != 3)
07264       return CLI_SHOWUSAGE;
07265 
07266    if (argc == 3) { /* specific queue */
07267       if ((q = load_realtime_queue(argv[2]))) {
07268          queue_t_unref(q, "Done with temporary pointer");
07269       }
07270    } else if (ast_check_realtime("queues")) {
07271       /* This block is to find any queues which are defined in realtime but
07272        * which have not yet been added to the in-core container
07273        */
07274       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
07275       char *queuename;
07276       if (cfg) {
07277          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
07278             if ((q = load_realtime_queue(queuename))) {
07279                queue_t_unref(q, "Done with temporary pointer");
07280             }
07281          }
07282          ast_config_destroy(cfg);
07283       }
07284    }
07285 
07286    ao2_lock(queues);
07287    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
07288    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07289       float sl;
07290       struct call_queue *realtime_queue = NULL;
07291 
07292       ao2_lock(q);
07293       /* This check is to make sure we don't print information for realtime
07294        * queues which have been deleted from realtime but which have not yet
07295        * been deleted from the in-core container. Only do this if we're not
07296        * looking for a specific queue.
07297        */
07298       if (argc < 3 && q->realtime) {
07299          realtime_queue = load_realtime_queue(q->name);
07300          if (!realtime_queue) {
07301             ao2_unlock(q);
07302             queue_t_unref(q, "Done with iterator");
07303             continue;
07304          }
07305          queue_t_unref(realtime_queue, "Queue is already in memory");
07306       }
07307 
07308       if (argc == 3 && strcasecmp(q->name, argv[2])) {
07309          ao2_unlock(q);
07310          queue_t_unref(q, "Done with iterator");
07311          continue;
07312       }
07313       found = 1;
07314 
07315       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
07316       if (q->maxlen)
07317          ast_str_append(&out, 0, "%d", q->maxlen);
07318       else
07319          ast_str_append(&out, 0, "unlimited");
07320       sl = 0;
07321       if (q->callscompleted > 0)
07322          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
07323       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
07324          int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
07325          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
07326       do_print(s, fd, ast_str_buffer(out));
07327       if (!ao2_container_count(q->members))
07328          do_print(s, fd, "   No Members");
07329       else {
07330          struct member *mem;
07331 
07332          do_print(s, fd, "   Members: ");
07333          mem_iter = ao2_iterator_init(q->members, 0);
07334          while ((mem = ao2_iterator_next(&mem_iter))) {
07335             ast_str_set(&out, 0, "      %s", mem->membername);
07336             if (strcasecmp(mem->membername, mem->interface)) {
07337                ast_str_append(&out, 0, " (%s)", mem->interface);
07338             }
07339             if (mem->penalty)
07340                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
07341             ast_str_append(&out, 0, "%s%s%s (%s)",
07342                mem->dynamic ? " (dynamic)" : "",
07343                mem->realtime ? " (realtime)" : "",
07344                mem->paused ? " (paused)" : "",
07345                ast_devstate2str(mem->status));
07346             if (mem->calls)
07347                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
07348                   mem->calls, (long) (time(NULL) - mem->lastcall));
07349             else
07350                ast_str_append(&out, 0, " has taken no calls yet");
07351             do_print(s, fd, ast_str_buffer(out));
07352             ao2_ref(mem, -1);
07353          }
07354          ao2_iterator_destroy(&mem_iter);
07355       }
07356       if (!q->head)
07357          do_print(s, fd, "   No Callers");
07358       else {
07359          struct queue_ent *qe;
07360          int pos = 1;
07361 
07362          do_print(s, fd, "   Callers: ");
07363          for (qe = q->head; qe; qe = qe->next) {
07364             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
07365                pos++, qe->chan->name, (long) (now - qe->start) / 60,
07366                (long) (now - qe->start) % 60, qe->prio);
07367             do_print(s, fd, ast_str_buffer(out));
07368          }
07369       }
07370       do_print(s, fd, ""); /* blank line between entries */
07371       ao2_unlock(q);
07372       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
07373    }
07374    ao2_iterator_destroy(&queue_iter);
07375    ao2_unlock(queues);
07376    if (!found) {
07377       if (argc == 3)
07378          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07379       else
07380          ast_str_set(&out, 0, "No queues.");
07381       do_print(s, fd, ast_str_buffer(out));
07382    }
07383    return CLI_SUCCESS;
07384 }

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

05556 {
05557    struct call_queue *q;
05558    struct member *new_member, *old_member;
05559    int res = RES_NOSUCHQUEUE;
05560 
05561    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05562     * short-circuits if the queue is already in memory. */
05563    if (!(q = load_realtime_queue(queuename)))
05564       return res;
05565 
05566    ao2_lock(q);
05567    if ((old_member = interface_exists(q, interface)) == NULL) {
05568       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05569          new_member->dynamic = 1;
05570          member_add_to_queue(q, new_member);
05571          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05572             "Queue: %s\r\n"
05573             "Location: %s\r\n"
05574             "MemberName: %s\r\n"
05575             "Membership: %s\r\n"
05576             "Penalty: %d\r\n"
05577             "CallsTaken: %d\r\n"
05578             "LastCall: %d\r\n"
05579             "Status: %d\r\n"
05580             "Paused: %d\r\n",
05581             q->name, new_member->interface, new_member->membername,
05582             "dynamic",
05583             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05584             new_member->status, new_member->paused);
05585          
05586          ao2_ref(new_member, -1);
05587          new_member = NULL;
05588 
05589          if (dump)
05590             dump_queue_members(q);
05591          
05592          res = RES_OKAY;
05593       } else {
05594          res = RES_OUTOFMEMORY;
05595       }
05596    } else {
05597       ao2_ref(old_member, -1);
05598       res = RES_EXISTS;
05599    }
05600    ao2_unlock(q);
05601    queue_t_unref(q, "Expiring temporary reference");
05602 
05603    return res;
05604 }

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

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

02266 {
02267    struct call_queue *q;
02268 
02269    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02270       if (ast_string_field_init(q, 64)) {
02271          queue_t_unref(q, "String field allocation failed");
02272          return NULL;
02273       }
02274       ast_string_field_set(q, name, queuename);
02275    }
02276    return q;
02277 }

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

AddQueueMember application.

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

05988 {
05989    int res=-1;
05990    char *parse, *temppos = NULL;
05991    AST_DECLARE_APP_ARGS(args,
05992       AST_APP_ARG(queuename);
05993       AST_APP_ARG(interface);
05994       AST_APP_ARG(penalty);
05995       AST_APP_ARG(options);
05996       AST_APP_ARG(membername);
05997       AST_APP_ARG(state_interface);
05998    );
05999    int penalty = 0;
06000 
06001    if (ast_strlen_zero(data)) {
06002       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
06003       return -1;
06004    }
06005 
06006    parse = ast_strdupa(data);
06007 
06008    AST_STANDARD_APP_ARGS(args, parse);
06009 
06010    if (ast_strlen_zero(args.interface)) {
06011       args.interface = ast_strdupa(chan->name);
06012       temppos = strrchr(args.interface, '-');
06013       if (temppos)
06014          *temppos = '\0';
06015    }
06016 
06017    if (!ast_strlen_zero(args.penalty)) {
06018       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
06019          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
06020          penalty = 0;
06021       }
06022    }
06023 
06024    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
06025    case RES_OKAY:
06026       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
06027       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
06028       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
06029       res = 0;
06030       break;
06031    case RES_EXISTS:
06032       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
06033       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
06034       res = 0;
06035       break;
06036    case RES_NOSUCHQUEUE:
06037       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
06038       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
06039       res = 0;
06040       break;
06041    case RES_OUTOFMEMORY:
06042       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
06043       break;
06044    }
06045 
06046    return res;
06047 }

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

References ast_channel_datastore_find().

Referenced by try_calling().

04536 {
04537    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04538 }

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

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

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

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

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

Referenced by try_calling().

04362 {
04363    /* disregarding penalty on too few members? */
04364    int membercount = ao2_container_count(q->members);
04365    unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04366 
04367    if (usepenalty) {
04368       if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) ||
04369          (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) {
04370          return -1;
04371       }
04372    } else {
04373       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04374            membercount, q->penaltymemberslimit);
04375    }
04376 
04377    switch (q->strategy) {
04378    case QUEUE_STRATEGY_RINGALL:
04379       /* Everyone equal, except for penalty */
04380       tmp->metric = mem->penalty * 1000000 * usepenalty;
04381       break;
04382    case QUEUE_STRATEGY_LINEAR:
04383       if (pos < qe->linpos) {
04384          tmp->metric = 1000 + pos;
04385       } else {
04386          if (pos > qe->linpos)
04387             /* Indicate there is another priority */
04388             qe->linwrapped = 1;
04389          tmp->metric = pos;
04390       }
04391       tmp->metric += mem->penalty * 1000000 * usepenalty;
04392       break;
04393    case QUEUE_STRATEGY_RRORDERED:
04394    case QUEUE_STRATEGY_RRMEMORY:
04395       pos = mem->queuepos;
04396       if (pos < q->rrpos) {
04397          tmp->metric = 1000 + pos;
04398       } else {
04399          if (pos > q->rrpos)
04400             /* Indicate there is another priority */
04401             q->wrapped = 1;
04402          tmp->metric = pos;
04403       }
04404       tmp->metric += mem->penalty * 1000000 * usepenalty;
04405       break;
04406    case QUEUE_STRATEGY_RANDOM:
04407       tmp->metric = ast_random() % 1000;
04408       tmp->metric += mem->penalty * 1000000 * usepenalty;
04409       break;
04410    case QUEUE_STRATEGY_WRANDOM:
04411       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04412       break;
04413    case QUEUE_STRATEGY_FEWESTCALLS:
04414       tmp->metric = mem->calls;
04415       tmp->metric += mem->penalty * 1000000 * usepenalty;
04416       break;
04417    case QUEUE_STRATEGY_LEASTRECENT:
04418       if (!mem->lastcall)
04419          tmp->metric = 0;
04420       else
04421          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04422       tmp->metric += mem->penalty * 1000000 * usepenalty;
04423       break;
04424    default:
04425       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04426       break;
04427    }
04428    return 0;
04429 }

static void callattempt_free ( struct callattempt doomed  )  [static]

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

02928 {
02929    if (doomed->member) {
02930       ao2_ref(doomed->member, -1);
02931    }
02932    ast_party_connected_line_free(&doomed->connected);
02933    ast_free(doomed);
02934 }

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

Definition at line 3144 of file app_queue.c.

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

Referenced by ring_entry().

03145 {
03146    if (call->member->paused) {
03147       ast_debug(1, "%s paused, can't receive call\n", call->interface);
03148       return 0;
03149    }
03150 
03151    if (!qe->parent->ringinuse && !member_status_available(call->member->status)) {
03152       ast_debug(1, "%s not available, can't receive call\n", call->interface);
03153       return 0;
03154    }
03155 
03156    if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
03157       || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
03158       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
03159          (call->lastqueue ? call->lastqueue->name : qe->parent->name),
03160          call->interface);
03161       return 0;
03162    }
03163 
03164    if (use_weight && compare_weight(qe->parent, call->member)) {
03165       ast_debug(1, "Priority queue delaying call to %s:%s\n",
03166          qe->parent->name, call->interface);
03167       return 0;
03168    }
03169 
03170    if (!qe->parent->ringinuse) {
03171       if (member_call_pending_set(call->member)) {
03172          ast_debug(1, "%s has another call pending, can't receive call\n",
03173             call->interface);
03174          return 0;
03175       }
03176 
03177       /*
03178        * The queue member is available.  Get current status to be sure
03179        * because the device state and extension state callbacks may
03180        * not have updated the status yet.
03181        */
03182       if (!member_status_available(get_queue_member_status(call->member))) {
03183          ast_debug(1, "%s actually not available, can't receive call\n",
03184             call->interface);
03185          member_call_pending_clear(call->member);
03186          return 0;
03187       }
03188    }
03189 
03190    return 1;
03191 }

static void clear_queue ( struct call_queue q  )  [static]

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

01795 {
01796    q->holdtime = 0;
01797    q->callscompleted = 0;
01798    q->callsabandoned = 0;
01799    q->callscompletedinsl = 0;
01800    q->talktime = 0;
01801 
01802    if (q->members) {
01803       struct member *mem;
01804       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01805       while ((mem = ao2_iterator_next(&mem_iter))) {
01806          mem->calls = 0;
01807          mem->lastcall = 0;
01808          ao2_ref(mem, -1);
01809       }
01810       ao2_iterator_destroy(&mem_iter);
01811    }
01812 }

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

07194 {
07195    struct call_queue *q;
07196    struct ao2_iterator queue_iter;
07197 
07198    queue_iter = ao2_iterator_init(queues, 0);
07199    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07200       ao2_lock(q);
07201       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
07202          clear_queue(q);
07203       ao2_unlock(q);
07204       queue_t_unref(q, "Done with iterator");
07205    }
07206    ao2_iterator_destroy(&queue_iter);
07207    return 0;
07208 }

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

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

03008 {
03009    struct call_queue *q;
03010    struct member *mem;
03011    int found = 0;
03012    struct ao2_iterator queue_iter;
03013 
03014    queue_iter = ao2_iterator_init(queues, 0);
03015    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03016       if (q == rq) { /* don't check myself, could deadlock */
03017          queue_t_unref(q, "Done with iterator");
03018          continue;
03019       }
03020       ao2_lock(q);
03021       if (q->count && q->members) {
03022          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
03023             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
03024             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
03025                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);
03026                found = 1;
03027             }
03028             ao2_ref(mem, -1);
03029          }
03030       }
03031       ao2_unlock(q);
03032       queue_t_unref(q, "Done with iterator");
03033       if (found) {
03034          break;
03035       }
03036    }
03037    ao2_iterator_destroy(&queue_iter);
03038    return found;
03039 }

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

07459 {
07460    struct call_queue *q;
07461    char *ret = NULL;
07462    int which = 0;
07463    int wordlen = strlen(word);
07464    struct ao2_iterator queue_iter;
07465    const char *word_list = NULL;
07466 
07467    /* for certain commands, already completed items should be left out of
07468     * the list */
07469    if (word_list_offset && strlen(line) >= word_list_offset) {
07470       word_list = line + word_list_offset;
07471    }
07472 
07473    queue_iter = ao2_iterator_init(queues, 0);
07474    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07475       if (!strncasecmp(word, q->name, wordlen) && ++which > state
07476          && (!word_list_offset || !word_in_list(word_list, q->name))) {
07477          ret = ast_strdup(q->name);
07478          queue_t_unref(q, "Done with iterator");
07479          break;
07480       }
07481       queue_t_unref(q, "Done with iterator");
07482    }
07483    ao2_iterator_destroy(&queue_iter);
07484 
07485    /* Pretend "rules" is at the end of the queues list in certain
07486     * circumstances since it is an alternate command that should be
07487     * tab-completable for "queue show" */
07488    if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
07489       ret = ast_strdup("rules");
07490    }
07491 
07492    return ret;
07493 }

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

Definition at line 7920 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07921 {
07922    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07923    switch (pos) {
07924    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07925       return NULL;
07926    case 4: /* only one possible match, "to" */
07927       return state == 0 ? ast_strdup("to") : NULL;
07928    case 5: /* <queue> */
07929       return complete_queue(line, word, pos, state, 0);
07930    case 6: /* only one possible match, "penalty" */
07931       return state == 0 ? ast_strdup("penalty") : NULL;
07932    case 7:
07933       if (state < 100) {      /* 0-99 */
07934          char *num;
07935          if ((num = ast_malloc(3))) {
07936             sprintf(num, "%d", state);
07937          }
07938          return num;
07939       } else {
07940          return NULL;
07941       }
07942    case 8: /* only one possible match, "as" */
07943       return state == 0 ? ast_strdup("as") : NULL;
07944    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
07945       return NULL;
07946    default:
07947       return NULL;
07948    }
07949 }

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

Definition at line 8142 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

08143 {
08144    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
08145    switch (pos) {
08146    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
08147       return NULL;
08148    case 4:  /* only one possible match, "queue" */
08149       return state == 0 ? ast_strdup("queue") : NULL;
08150    case 5:  /* <queue> */
08151       return complete_queue(line, word, pos, state, 0);
08152    case 6: /* "reason" */
08153       return state == 0 ? ast_strdup("reason") : NULL;
08154    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
08155       return NULL;
08156    default:
08157       return NULL;
08158    }
08159 }

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

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

08051 {
08052    int which = 0;
08053    struct call_queue *q;
08054    struct member *m;
08055    struct ao2_iterator queue_iter;
08056    struct ao2_iterator mem_iter;
08057    int wordlen = strlen(word);
08058 
08059    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
08060    if (pos > 5 || pos < 3)
08061       return NULL;
08062    if (pos == 4)   /* only one possible match, 'from' */
08063       return (state == 0 ? ast_strdup("from") : NULL);
08064 
08065    if (pos == 5) {  /* No need to duplicate code */
08066       return complete_queue(line, word, pos, state, 0);
08067    }
08068 
08069    /* here is the case for 3, <member> */
08070    queue_iter = ao2_iterator_init(queues, 0);
08071    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
08072       ao2_lock(q);
08073       mem_iter = ao2_iterator_init(q->members, 0);
08074       while ((m = ao2_iterator_next(&mem_iter))) {
08075          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
08076             char *tmp;
08077             tmp = ast_strdup(m->interface);
08078             ao2_ref(m, -1);
08079             ao2_iterator_destroy(&mem_iter);
08080             ao2_unlock(q);
08081             queue_t_unref(q, "Done with iterator, returning interface name");
08082             ao2_iterator_destroy(&queue_iter);
08083             return tmp;
08084          }
08085          ao2_ref(m, -1);
08086       }
08087       ao2_iterator_destroy(&mem_iter);
08088       ao2_unlock(q);
08089       queue_t_unref(q, "Done with iterator");
08090    }
08091    ao2_iterator_destroy(&queue_iter);
08092 
08093    return NULL;
08094 }

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

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

08276 {
08277    int which = 0;
08278    struct rule_list *rl_iter;
08279    int wordlen = strlen(word);
08280    char *ret = NULL;
08281    if (pos != 3) /* Wha? */ {
08282       return NULL;
08283    }
08284 
08285    AST_LIST_LOCK(&rule_lists);
08286    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08287       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
08288          ret = ast_strdup(rl_iter->name);
08289          break;
08290       }
08291    }
08292    AST_LIST_UNLOCK(&rule_lists);
08293 
08294    return ret;
08295 }

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

Definition at line 8212 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

08213 {
08214    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
08215    switch (pos) {
08216    case 4:
08217       if (state == 0) {
08218          return ast_strdup("on");
08219       } else {
08220          return NULL;
08221       }
08222    case 6:
08223       if (state == 0) {
08224          return ast_strdup("in");
08225       } else {
08226          return NULL;
08227       }
08228    case 7:
08229       return complete_queue(line, word, pos, state, 0);
08230    default:
08231       return NULL;
08232    }
08233 }

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

Definition at line 7495 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07496 {
07497    if (pos == 2) {
07498       return complete_queue(line, word, pos, state, 0);
07499    }
07500    return NULL;
07501 }

static int compress_char ( const char  c  )  [static]

Definition at line 1685 of file app_queue.c.

Referenced by member_hash_fn().

01686 {
01687    if (c < 32)
01688       return 0;
01689    else if (c > 96)
01690       return c - 64;
01691    else
01692       return c - 32;
01693 }

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

Copy rule from global list into specified queue.

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

06085 {
06086    struct penalty_rule *pr_iter;
06087    struct rule_list *rl_iter;
06088    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
06089    AST_LIST_LOCK(&rule_lists);
06090    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06091       if (!strcasecmp(rl_iter->name, tmp))
06092          break;
06093    }
06094    if (rl_iter) {
06095       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06096          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
06097          if (!new_pr) {
06098             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
06099             break;
06100          }
06101          new_pr->time = pr_iter->time;
06102          new_pr->max_value = pr_iter->max_value;
06103          new_pr->min_value = pr_iter->min_value;
06104          new_pr->max_relative = pr_iter->max_relative;
06105          new_pr->min_relative = pr_iter->min_relative;
06106          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
06107       }
06108    }
06109    AST_LIST_UNLOCK(&rule_lists);
06110 }

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

01654 {
01655    struct member *cur;
01656    
01657    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01658       cur->penalty = penalty;
01659       cur->paused = paused;
01660       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01661       if (!ast_strlen_zero(state_interface))
01662          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01663       else
01664          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01665       if (!ast_strlen_zero(membername))
01666          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01667       else
01668          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01669       if (!strchr(cur->interface, '/'))
01670          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01671       if (!strncmp(cur->state_interface, "hint:", 5)) {
01672          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01673          char *exten = strsep(&context, "@") + 5;
01674 
01675          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01676          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01677       }
01678       cur->status = get_queue_member_status(cur);
01679    }
01680 
01681    return cur;
01682 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

02252 {
02253    struct call_queue *q = obj;
02254    int i;
02255 
02256    free_members(q, 1);
02257    ast_string_field_free_memory(q);
02258    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02259       if (q->sound_periodicannounce[i])
02260          free(q->sound_periodicannounce[i]);
02261    }
02262    ao2_ref(q->members, -1);
02263 }

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

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

01552 {
01553    enum ast_device_state state;
01554    const char *device;
01555    struct statechange *sc;
01556    size_t datapsize;
01557 
01558    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01559    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01560 
01561    if (ast_strlen_zero(device)) {
01562       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01563       return;
01564    }
01565    datapsize = sizeof(*sc) + strlen(device) + 1;
01566    if (!(sc = ast_calloc(1, datapsize))) {
01567       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01568       return;
01569    }
01570    sc->state = state;
01571    strcpy(sc->dev, device);
01572    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01573       ast_free(sc);
01574    }
01575 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 3042 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

03043 {
03044    o->stillgoing = 0;
03045    ast_hangup(o->chan);
03046    o->chan = NULL;
03047 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

07241 {
07242    if (s)
07243       astman_append(s, "%s\r\n", str);
07244    else
07245       ast_cli(fd, "%s\n", str);
07246 }

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

05456 {
05457    struct member *cur_member;
05458    struct ast_str *value;
05459    struct ao2_iterator mem_iter;
05460 
05461    if (!pm_queue) {
05462       return;
05463    }
05464 
05465    /* 4K is a reasonable default for most applications, but we grow to
05466     * accommodate more if necessary. */
05467    if (!(value = ast_str_create(4096))) {
05468       return;
05469    }
05470 
05471    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05472    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05473       if (!cur_member->dynamic) {
05474          ao2_ref(cur_member, -1);
05475          continue;
05476       }
05477 
05478       ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s",
05479          ast_str_strlen(value) ? "|" : "",
05480          cur_member->interface,
05481          cur_member->penalty,
05482          cur_member->paused,
05483          cur_member->membername,
05484          cur_member->state_interface);
05485 
05486       ao2_ref(cur_member, -1);
05487    }
05488    ao2_iterator_destroy(&mem_iter);
05489 
05490    if (ast_str_strlen(value) && !cur_member) {
05491       if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value)))
05492          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05493    } else {
05494       /* Delete the entry if the queue is empty or there is an error */
05495       ast_db_del(pm_family, pm_queue->name);
05496    }
05497 
05498    ast_free(value);
05499 }

static void end_bridge_callback ( void *  data  )  [static]

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

04584 {
04585    struct queue_end_bridge *qeb = data;
04586    struct call_queue *q = qeb->q;
04587    struct ast_channel *chan = qeb->chan;
04588 
04589    if (ao2_ref(qeb, -1) == 1) {
04590       set_queue_variables(q, chan);
04591       /* This unrefs the reference we made in try_calling when we allocated qeb */
04592       queue_t_unref(q, "Expire bridge_config reference");
04593    }
04594 }

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

Definition at line 4576 of file app_queue.c.

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

Referenced by try_calling().

04577 {
04578    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04579    ao2_ref(qeb, +1);
04580    qeb->chan = originator;
04581 }

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

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

01610 {
01611    struct ao2_iterator miter, qiter;
01612    struct member *m;
01613    struct call_queue *q;
01614    int found = 0, device_state = extensionstate2devicestate(state);
01615 
01616    qiter = ao2_iterator_init(queues, 0);
01617    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01618       ao2_lock(q);
01619 
01620       miter = ao2_iterator_init(q->members, 0);
01621       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01622          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01623             update_status(q, m, device_state);
01624             ao2_ref(m, -1);
01625             found = 1;
01626             break;
01627          }
01628       }
01629       ao2_iterator_destroy(&miter);
01630 
01631       ao2_unlock(q);
01632       queue_t_unref(q, "Done with iterator");
01633    }
01634    ao2_iterator_destroy(&qiter);
01635 
01636         if (found) {
01637       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01638    } else {
01639       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",
01640            exten, context, device_state, ast_devstate2str(device_state));
01641    }
01642 
01643    return 0;
01644 }

static int extensionstate2devicestate ( int  state  )  [static]

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

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

01579 {
01580    switch (state) {
01581    case AST_EXTENSION_NOT_INUSE:
01582       state = AST_DEVICE_NOT_INUSE;
01583       break;
01584    case AST_EXTENSION_INUSE:
01585       state = AST_DEVICE_INUSE;
01586       break;
01587    case AST_EXTENSION_BUSY:
01588       state = AST_DEVICE_BUSY;
01589       break;
01590    case AST_EXTENSION_RINGING:
01591       state = AST_DEVICE_RINGING;
01592       break;
01593    case AST_EXTENSION_ONHOLD:
01594       state = AST_DEVICE_ONHOLD;
01595       break;
01596    case AST_EXTENSION_UNAVAILABLE:
01597       state = AST_DEVICE_UNAVAILABLE;
01598       break;
01599    case AST_EXTENSION_REMOVED:
01600    case AST_EXTENSION_DEACTIVATED:
01601    default:
01602       state = AST_DEVICE_INVALID;
01603       break;
01604    }
01605 
01606    return state;
01607 }

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

find the entry with the best metric, or NULL

Definition at line 3365 of file app_queue.c.

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

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

03366 {
03367    struct callattempt *best = NULL, *cur;
03368 
03369    for (cur = outgoing; cur; cur = cur->q_next) {
03370       if (cur->stillgoing &&              /* Not already done */
03371          !cur->chan &&              /* Isn't already going */
03372          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03373          best = cur;
03374       }
03375    }
03376 
03377    return best;
03378 }

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

02290 {
02291    struct ast_variable *v;
02292    struct call_queue *q, tmpq = {
02293       .name = queuename,   
02294    };
02295    struct member *m;
02296    struct ao2_iterator mem_iter;
02297    char *interface = NULL;
02298    const char *tmp_name;
02299    char *tmp;
02300    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
02301 
02302    /* Static queues override realtime. */
02303    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02304       ao2_lock(q);
02305       if (!q->realtime) {
02306          if (q->dead) {
02307             ao2_unlock(q);
02308             queue_t_unref(q, "Queue is dead; can't return it");
02309             return NULL;
02310          } else {
02311             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02312             ao2_unlock(q);
02313             return q;
02314          }
02315       }
02316    } else if (!member_config)
02317       /* Not found in the list, and it's not realtime ... */
02318       return NULL;
02319 
02320    /* Check if queue is defined in realtime. */
02321    if (!queue_vars) {
02322       /* Delete queue from in-core list if it has been deleted in realtime. */
02323       if (q) {
02324          /*! \note Hmm, can't seem to distinguish a DB failure from a not
02325             found condition... So we might delete an in-core queue
02326             in case of DB failure. */
02327          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02328 
02329          q->dead = 1;
02330          /* Delete if unused (else will be deleted when last caller leaves). */
02331          queues_t_unlink(queues, q, "Unused; removing from container");
02332          ao2_unlock(q);
02333          queue_t_unref(q, "Queue is dead; can't return it");
02334       }
02335       return NULL;
02336    }
02337 
02338    /* Create a new queue if an in-core entry does not exist yet. */
02339    if (!q) {
02340       struct ast_variable *tmpvar = NULL;
02341       if (!(q = alloc_queue(queuename)))
02342          return NULL;
02343       ao2_lock(q);
02344       clear_queue(q);
02345       q->realtime = 1;
02346       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
02347        * will allocate the members properly
02348        */
02349       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02350          if (!strcasecmp(tmpvar->name, "strategy")) {
02351             q->strategy = strat2int(tmpvar->value);
02352             if (q->strategy < 0) {
02353                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02354                tmpvar->value, q->name);
02355                q->strategy = QUEUE_STRATEGY_RINGALL;
02356             }
02357             break;
02358          }
02359       }
02360       /* We traversed all variables and didn't find a strategy */
02361       if (!tmpvar)
02362          q->strategy = QUEUE_STRATEGY_RINGALL;
02363       queues_t_link(queues, q, "Add queue to container");
02364    }
02365    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
02366 
02367    memset(tmpbuf, 0, sizeof(tmpbuf));
02368    for (v = queue_vars; v; v = v->next) {
02369       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
02370       if (strchr(v->name, '_')) {
02371          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02372          tmp_name = tmpbuf;
02373          tmp = tmpbuf;
02374          while ((tmp = strchr(tmp, '_')))
02375             *tmp++ = '-';
02376       } else
02377          tmp_name = v->name;
02378 
02379       /* NULL values don't get returned from realtime; blank values should
02380        * still get set.  If someone doesn't want a value to be set, they
02381        * should set the realtime column to NULL, not blank. */
02382       queue_set_param(q, tmp_name, v->value, -1, 0);
02383    }
02384 
02385    /* Temporarily set realtime members dead so we can detect deleted ones. */
02386    mem_iter = ao2_iterator_init(q->members, 0);
02387    while ((m = ao2_iterator_next(&mem_iter))) {
02388       if (m->realtime)
02389          m->dead = 1;
02390       ao2_ref(m, -1);
02391    }
02392    ao2_iterator_destroy(&mem_iter);
02393 
02394    while ((interface = ast_category_browse(member_config, interface))) {
02395       rt_handle_member_record(q, interface,
02396          ast_variable_retrieve(member_config, interface, "uniqueid"),
02397          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02398          ast_variable_retrieve(member_config, interface, "penalty"),
02399          ast_variable_retrieve(member_config, interface, "paused"),
02400          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02401    }
02402 
02403    /* Delete all realtime members that have been deleted in DB. */
02404    mem_iter = ao2_iterator_init(q->members, 0);
02405    while ((m = ao2_iterator_next(&mem_iter))) {
02406       if (m->dead) {
02407          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02408          member_remove_from_queue(q, m);
02409       }
02410       ao2_ref(m, -1);
02411    }
02412    ao2_iterator_destroy(&mem_iter);
02413 
02414    ao2_unlock(q);
02415 
02416    return q;
02417 }

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

Iterate through queue's member list and delete them.

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

02236 {
02237    /* Free non-dynamic members */
02238    struct member *cur;
02239    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02240 
02241    while ((cur = ao2_iterator_next(&mem_iter))) {
02242       if (all || !cur->dynamic) {
02243          member_remove_from_queue(q, cur);
02244       }
02245       ao2_ref(cur, -1);
02246    }
02247    ao2_iterator_destroy(&mem_iter);
02248 }

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

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

05733 {
05734    int foundqueue = 0, penalty;
05735    struct call_queue *q, tmpq = {
05736       .name = queuename,   
05737    };
05738    struct member *mem;
05739    
05740    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05741       foundqueue = 1;
05742       ao2_lock(q);
05743       if ((mem = interface_exists(q, interface))) {
05744          penalty = mem->penalty;
05745          ao2_ref(mem, -1);
05746          ao2_unlock(q);
05747          queue_t_unref(q, "Search complete");
05748          return penalty;
05749       }
05750       ao2_unlock(q);
05751       queue_t_unref(q, "Search complete");
05752    }
05753 
05754    /* some useful debuging */
05755    if (foundqueue) 
05756       ast_log (LOG_ERROR, "Invalid queuename\n");
05757    else 
05758       ast_log (LOG_ERROR, "Invalid interface\n");
05759 
05760    return RESULT_FAILURE;
05761 }

static int get_member_status ( struct call_queue q,
int  max_penalty,
int  min_penalty,
enum empty_conditions  conditions 
) [static]

Check if members are available.

This function checks to see if members are available to be called. If any member is available, the function immediately returns 0. If no members are available, then -1 is returned.

Definition at line 1401 of file app_queue.c.

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

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

01402 {
01403    struct member *member;
01404    struct ao2_iterator mem_iter;
01405 
01406    ao2_lock(q);
01407    mem_iter = ao2_iterator_init(q->members, 0);
01408    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01409       if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) {
01410          if (conditions & QUEUE_EMPTY_PENALTY) {
01411             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01412             continue;
01413          }
01414       }
01415 
01416       switch (member->status) {
01417       case AST_DEVICE_INVALID:
01418          if (conditions & QUEUE_EMPTY_INVALID) {
01419             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01420             break;
01421          }
01422          goto default_case;
01423       case AST_DEVICE_UNAVAILABLE:
01424          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01425             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01426             break;
01427          }
01428          goto default_case;
01429       case AST_DEVICE_INUSE:
01430          if (conditions & QUEUE_EMPTY_INUSE) {
01431             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01432             break;
01433          }
01434          goto default_case;
01435       case AST_DEVICE_RINGING:
01436          if (conditions & QUEUE_EMPTY_RINGING) {
01437             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01438             break;
01439          }
01440          goto default_case;
01441       case AST_DEVICE_UNKNOWN:
01442          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01443             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01444             break;
01445          }
01446          /* Fall-through */
01447       default:
01448       default_case:
01449          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01450             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01451             break;
01452          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01453             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);
01454             break;
01455          } else {
01456             ao2_ref(member, -1);
01457             ao2_iterator_destroy(&mem_iter);
01458             ao2_unlock(q);
01459             ast_debug(4, "%s is available.\n", member->membername);
01460             return 0;
01461          }
01462          break;
01463       }
01464    }
01465    ao2_iterator_destroy(&mem_iter);
01466 
01467    ao2_unlock(q);
01468    return -1;
01469 }

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

07977 {
07978    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07979    int penalty;
07980 
07981    switch ( cmd ) {
07982    case CLI_INIT:
07983       e->command = "queue add member";
07984       e->usage =
07985          "Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07986          "       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";
07987       return NULL;
07988    case CLI_GENERATE:
07989       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07990    }
07991 
07992    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07993       return CLI_SHOWUSAGE;
07994    } else if (strcmp(a->argv[4], "to")) {
07995       return CLI_SHOWUSAGE;
07996    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07997       return CLI_SHOWUSAGE;
07998    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07999       return CLI_SHOWUSAGE;
08000    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
08001       return CLI_SHOWUSAGE;
08002    }
08003 
08004    queuename = a->argv[5];
08005    interface = a->argv[3];
08006    if (a->argc >= 8) {
08007       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
08008          if (penalty < 0) {
08009             ast_cli(a->fd, "Penalty must be >= 0\n");
08010             penalty = 0;
08011          }
08012       } else {
08013          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
08014          penalty = 0;
08015       }
08016    } else {
08017       penalty = 0;
08018    }
08019 
08020    if (a->argc >= 10) {
08021       membername = a->argv[9];
08022    }
08023 
08024    if (a->argc >= 12) {
08025       state_interface = a->argv[11];
08026    }
08027 
08028    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
08029    case RES_OKAY:
08030       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
08031       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
08032       return CLI_SUCCESS;
08033    case RES_EXISTS:
08034       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
08035       return CLI_FAILURE;
08036    case RES_NOSUCHQUEUE:
08037       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
08038       return CLI_FAILURE;
08039    case RES_OUTOFMEMORY:
08040       ast_cli(a->fd, "Out of memory\n");
08041       return CLI_FAILURE;
08042    case RES_NOT_DYNAMIC:
08043       ast_cli(a->fd, "Member not dynamic\n");
08044       return CLI_FAILURE;
08045    default:
08046       return CLI_FAILURE;
08047    }
08048 }

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

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

08162 {
08163    const char *queuename, *interface, *reason;
08164    int paused;
08165 
08166    switch (cmd) {
08167    case CLI_INIT:
08168       e->command = "queue {pause|unpause} member";
08169       e->usage = 
08170          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
08171          "  Pause or unpause a queue member. Not specifying a particular queue\n"
08172          "  will pause or unpause a member across all queues to which the member\n"
08173          "  belongs.\n";
08174       return NULL;
08175    case CLI_GENERATE:
08176       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
08177    }
08178 
08179    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
08180       return CLI_SHOWUSAGE;
08181    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
08182       return CLI_SHOWUSAGE;
08183    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
08184       return CLI_SHOWUSAGE;
08185    }
08186 
08187 
08188    interface = a->argv[3];
08189    queuename = a->argc >= 6 ? a->argv[5] : NULL;
08190    reason = a->argc == 8 ? a->argv[7] : NULL;
08191    paused = !strcasecmp(a->argv[1], "pause");
08192 
08193    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
08194       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
08195       if (!ast_strlen_zero(queuename))
08196          ast_cli(a->fd, " in queue '%s'", queuename);
08197       if (!ast_strlen_zero(reason))
08198          ast_cli(a->fd, " for reason '%s'", reason);
08199       ast_cli(a->fd, "\n");
08200       return CLI_SUCCESS;
08201    } else {
08202       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
08203       if (!ast_strlen_zero(queuename))
08204          ast_cli(a->fd, " in queue '%s'", queuename);
08205       if (!ast_strlen_zero(reason))
08206          ast_cli(a->fd, " for reason '%s'", reason);
08207       ast_cli(a->fd, "\n");
08208       return CLI_FAILURE;
08209    }
08210 }

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

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

08371 {
08372    struct ast_flags mask = {0,};
08373    int i;
08374 
08375    switch (cmd) {
08376       case CLI_INIT:
08377          e->command = "queue reload {parameters|members|rules|all}";
08378          e->usage =
08379             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08380             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08381             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08382             "specified in order to know what information to reload. Below is an explanation\n"
08383             "of each of these qualifiers.\n"
08384             "\n"
08385             "\t'members' - reload queue members from queues.conf\n"
08386             "\t'parameters' - reload all queue options except for queue members\n"
08387             "\t'rules' - reload the queuerules.conf file\n"
08388             "\t'all' - reload queue rules, parameters, and members\n"
08389             "\n"
08390             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08391             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08392             "one queue is specified when using this command, reloading queue rules may cause\n"
08393             "other queues to be affected\n";
08394          return NULL;
08395       case CLI_GENERATE:
08396          if (a->pos >= 3) {
08397             /* find the point at which the list of queue names starts */
08398             const char *command_end = a->line + strlen("queue reload ");
08399             command_end = strchr(command_end, ' ');
08400             if (!command_end) {
08401                command_end = a->line + strlen(a->line);
08402             }
08403             return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
08404          } else {
08405             return NULL;
08406          }
08407    }
08408 
08409    if (a->argc < 3)
08410       return CLI_SHOWUSAGE;
08411 
08412    if (!strcasecmp(a->argv[2], "rules")) {
08413       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08414    } else if (!strcasecmp(a->argv[2], "members")) {
08415       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08416    } else if (!strcasecmp(a->argv[2], "parameters")) {
08417       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08418    } else if (!strcasecmp(a->argv[2], "all")) {
08419       ast_set_flag(&mask, AST_FLAGS_ALL);
08420    }
08421 
08422    if (a->argc == 3) {
08423       reload_handler(1, &mask, NULL);
08424       return CLI_SUCCESS;
08425    }
08426 
08427    for (i = 3; i < a->argc; ++i) {
08428       reload_handler(1, &mask, a->argv[i]);
08429    }
08430 
08431    return CLI_SUCCESS;
08432 }

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

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

08097 {
08098    const char *queuename, *interface;
08099 
08100    switch (cmd) {
08101    case CLI_INIT:
08102       e->command = "queue remove member";
08103       e->usage = 
08104          "Usage: queue remove member <channel> from <queue>\n"
08105          "       Remove a specific channel from a queue.\n";
08106       return NULL;
08107    case CLI_GENERATE:
08108       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
08109    }
08110 
08111    if (a->argc != 6) {
08112       return CLI_SHOWUSAGE;
08113    } else if (strcmp(a->argv[4], "from")) {
08114       return CLI_SHOWUSAGE;
08115    }
08116 
08117    queuename = a->argv[5];
08118    interface = a->argv[3];
08119 
08120    switch (remove_from_queue(queuename, interface)) {
08121    case RES_OKAY:
08122       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
08123       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
08124       return CLI_SUCCESS;
08125    case RES_EXISTS:
08126       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
08127       return CLI_FAILURE;
08128    case RES_NOSUCHQUEUE:
08129       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
08130       return CLI_FAILURE;
08131    case RES_OUTOFMEMORY:
08132       ast_cli(a->fd, "Out of memory\n");
08133       return CLI_FAILURE;
08134    case RES_NOT_DYNAMIC:
08135       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
08136       return CLI_FAILURE;
08137    default:
08138       return CLI_FAILURE;
08139    }
08140 }

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

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

08332 {
08333    struct ast_flags mask = {QUEUE_RESET_STATS,};
08334    int i;
08335 
08336    switch (cmd) {
08337       case CLI_INIT:
08338          e->command = "queue reset stats";
08339          e->usage =
08340             "Usage: queue reset stats [<queuenames>]\n"
08341             "\n"
08342             "Issuing this command will reset statistics for\n"
08343             "<queuenames>, or for all queues if no queue is\n"
08344             "specified.\n";
08345          return NULL;
08346       case CLI_GENERATE:
08347          if (a->pos >= 3) {
08348             return complete_queue(a->line, a->word, a->pos, a->n, 17);
08349          } else {
08350             return NULL;
08351          }
08352    }
08353 
08354    if (a->argc < 3) {
08355       return CLI_SHOWUSAGE;
08356    }
08357 
08358    if (a->argc == 3) {
08359       reload_handler(1, &mask, NULL);
08360       return CLI_SUCCESS;
08361    }
08362 
08363    for (i = 3; i < a->argc; ++i) {
08364       reload_handler(1, &mask, a->argv[i]);
08365    }
08366 
08367    return CLI_SUCCESS;
08368 }

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

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

08298 {
08299    const char *rule;
08300    struct rule_list *rl_iter;
08301    struct penalty_rule *pr_iter;
08302    switch (cmd) {
08303    case CLI_INIT:
08304       e->command = "queue show rules";
08305       e->usage =
08306       "Usage: queue show rules [rulename]\n"
08307       "  Show the list of rules associated with rulename. If no\n"
08308       "  rulename is specified, list all rules defined in queuerules.conf\n";
08309       return NULL;
08310    case CLI_GENERATE:
08311       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
08312    }
08313 
08314    if (a->argc != 3 && a->argc != 4)
08315       return CLI_SHOWUSAGE;
08316 
08317    rule = a->argc == 4 ? a->argv[3] : "";
08318    AST_LIST_LOCK(&rule_lists);
08319    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08320       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
08321          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
08322          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
08323             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);
08324          }
08325       }
08326    }
08327    AST_LIST_UNLOCK(&rule_lists);
08328    return CLI_SUCCESS; 
08329 }

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

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

08236 {
08237    const char *queuename = NULL, *interface;
08238    int penalty = 0;
08239 
08240    switch (cmd) {
08241    case CLI_INIT:
08242       e->command = "queue set penalty";
08243       e->usage = 
08244       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
08245       "  Set a member's penalty in the queue specified. If no queue is specified\n"
08246       "  then that interface's penalty is set in all queues to which that interface is a member\n";
08247       return NULL;
08248    case CLI_GENERATE:
08249       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
08250    }
08251 
08252    if (a->argc != 6 && a->argc != 8) {
08253       return CLI_SHOWUSAGE;
08254    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
08255       return CLI_SHOWUSAGE;
08256    }
08257 
08258    if (a->argc == 8)
08259       queuename = a->argv[7];
08260    interface = a->argv[5];
08261    penalty = atoi(a->argv[3]);
08262 
08263    switch (set_member_penalty(queuename, interface, penalty)) {
08264    case RESULT_SUCCESS:
08265       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08266       return CLI_SUCCESS;
08267    case RESULT_FAILURE:
08268       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08269       return CLI_FAILURE;
08270    default:
08271       return CLI_FAILURE;
08272    }
08273 }

static int handle_statechange ( void *  datap  )  [static]

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

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

01508 {
01509    struct statechange *sc = datap;
01510    struct ao2_iterator miter, qiter;
01511    struct member *m;
01512    struct call_queue *q;
01513    char interface[80], *slash_pos;
01514    int found = 0;
01515 
01516    qiter = ao2_iterator_init(queues, 0);
01517    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01518       ao2_lock(q);
01519 
01520       miter = ao2_iterator_init(q->members, 0);
01521       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01522          ast_copy_string(interface, m->state_interface, sizeof(interface));
01523 
01524          if ((slash_pos = strchr(interface, '/')))
01525             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01526                *slash_pos = '\0';
01527 
01528          if (!strcasecmp(interface, sc->dev)) {
01529             found = 1;
01530             update_status(q, m, sc->state);
01531             ao2_ref(m, -1);
01532             break;
01533          }
01534       }
01535       ao2_iterator_destroy(&miter);
01536 
01537       ao2_unlock(q);
01538       queue_t_unref(q, "Done with iterator");
01539    }
01540    ao2_iterator_destroy(&qiter);
01541 
01542    if (found)
01543       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01544    else
01545       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));
01546 
01547    ast_free(sc);
01548    return 0;
01549 }

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

02938 {
02939    struct callattempt *oo;
02940 
02941    while (outgoing) {
02942       /* If someone else answered the call we should indicate this in the CANCEL */
02943       /* Hangup any existing lines we have open */
02944       if (outgoing->chan && (outgoing->chan != exception)) {
02945          if (exception || cancel_answered_elsewhere)
02946             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02947          ast_hangup(outgoing->chan);
02948       }
02949       oo = outgoing;
02950       outgoing = outgoing->q_next;
02951       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
02952       callattempt_free(oo);
02953    }
02954 }

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 1717 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::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().

01718 {
01719    int i;
01720    struct penalty_rule *pr_iter;
01721 
01722    q->dead = 0;
01723    q->retry = DEFAULT_RETRY;
01724    q->timeout = DEFAULT_TIMEOUT;
01725    q->maxlen = 0;
01726    q->announcefrequency = 0;
01727    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01728    q->announceholdtime = 1;
01729    q->announcepositionlimit = 10; /* Default 10 positions */
01730    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01731    q->roundingseconds = 0; /* Default - don't announce seconds */
01732    q->servicelevel = 0;
01733    q->ringinuse = 1;
01734    q->announce_to_first_user = 0;
01735    q->setinterfacevar = 0;
01736    q->setqueuevar = 0;
01737    q->setqueueentryvar = 0;
01738    q->autofill = autofill_default;
01739    q->montype = montype_default;
01740    q->monfmt[0] = '\0';
01741    q->reportholdtime = 0;
01742    q->wrapuptime = 0;
01743    q->penaltymemberslimit = 0;
01744    q->joinempty = 0;
01745    q->leavewhenempty = 0;
01746    q->memberdelay = 0;
01747    q->maskmemberstatus = 0;
01748    q->eventwhencalled = 0;
01749    q->weight = 0;
01750    q->timeoutrestart = 0;
01751    q->periodicannouncefrequency = 0;
01752    q->randomperiodicannounce = 0;
01753    q->numperiodicannounce = 0;
01754    q->autopause = QUEUE_AUTOPAUSE_OFF;
01755    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01756    if (!q->members) {
01757       if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
01758          /* linear strategy depends on order, so we have to place all members in a single bucket */
01759          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01760       else
01761          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01762    }
01763    q->found = 1;
01764 
01765    ast_string_field_set(q, sound_next, "queue-youarenext");
01766    ast_string_field_set(q, sound_thereare, "queue-thereare");
01767    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01768    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01769    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01770    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01771    ast_string_field_set(q, sound_minutes, "queue-minutes");
01772    ast_string_field_set(q, sound_minute, "queue-minute");
01773    ast_string_field_set(q, sound_seconds, "queue-seconds");
01774    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01775    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01776 
01777    if (!q->sound_periodicannounce[0]) {
01778       q->sound_periodicannounce[0] = ast_str_create(32);
01779    }
01780 
01781    if (q->sound_periodicannounce[0]) {
01782       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01783    }
01784 
01785    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01786       if (q->sound_periodicannounce[i])
01787          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01788    }
01789 
01790    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01791       ast_free(pr_iter);
01792 }

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

References call_queue::head, and queue_ref().

Referenced by join_queue().

01372 {
01373    struct queue_ent *cur;
01374 
01375    if (!q || !new)
01376       return;
01377    if (prev) {
01378       cur = prev->next;
01379       prev->next = new;
01380    } else {
01381       cur = q->head;
01382       q->head = new;
01383    }
01384    new->next = cur;
01385 
01386    /* every queue_ent must have a reference to it's parent call_queue, this
01387     * reference does not go away until the end of the queue_ent's life, meaning
01388     * that even when the queue_ent leaves the call_queue this ref must remain. */
01389    queue_ref(q);
01390    new->parent = q;
01391    new->pos = ++(*pos);
01392    new->opos = *pos;
01393 }

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

01824 {
01825    char *timestr, *maxstr, *minstr, *contentdup;
01826    struct penalty_rule *rule = NULL, *rule_iter;
01827    struct rule_list *rl_iter;
01828    int penaltychangetime, inserted = 0;
01829 
01830    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01831       return -1;
01832    }
01833 
01834    contentdup = ast_strdupa(content);
01835    
01836    if (!(maxstr = strchr(contentdup, ','))) {
01837       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01838       ast_free(rule);
01839       return -1;
01840    }
01841 
01842    *maxstr++ = '\0';
01843    timestr = contentdup;
01844 
01845    if ((penaltychangetime = atoi(timestr)) < 0) {
01846       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01847       ast_free(rule);
01848       return -1;
01849    }
01850 
01851    rule->time = penaltychangetime;
01852 
01853    if ((minstr = strchr(maxstr,',')))
01854       *minstr++ = '\0';
01855    
01856    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01857     * OR if a min penalty change is indicated but no max penalty change is */
01858    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01859       rule->max_relative = 1;
01860    }
01861 
01862    rule->max_value = atoi(maxstr);
01863 
01864    if (!ast_strlen_zero(minstr)) {
01865       if (*minstr == '+' || *minstr == '-')
01866          rule->min_relative = 1;
01867       rule->min_value = atoi(minstr);
01868    } else /*there was no minimum specified, so assume this means no change*/
01869       rule->min_relative = 1;
01870 
01871    /*We have the rule made, now we need to insert it where it belongs*/
01872    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01873       if (strcasecmp(rl_iter->name, list_name))
01874          continue;
01875 
01876       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01877          if (rule->time < rule_iter->time) {
01878             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01879             inserted = 1;
01880             break;
01881          }
01882       }
01883       AST_LIST_TRAVERSE_SAFE_END;
01884    
01885       if (!inserted) {
01886          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01887          inserted = 1;
01888       }
01889 
01890       break;
01891    }
01892 
01893    if (!inserted) {
01894       ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
01895       ast_free(rule);
01896       return -1;
01897    }
01898    return 0;
01899 }

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

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

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

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

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

05430 {
05431    struct member *mem;
05432    struct ao2_iterator mem_iter;
05433 
05434    if (!q)
05435       return NULL;
05436 
05437    mem_iter = ao2_iterator_init(q->members, 0);
05438    while ((mem = ao2_iterator_next(&mem_iter))) {
05439       if (!strcasecmp(interface, mem->interface)) {
05440          ao2_iterator_destroy(&mem_iter);
05441          return mem;
05442       }
05443       ao2_ref(mem, -1);
05444    }
05445    ao2_iterator_destroy(&mem_iter);
05446 
05447    return NULL;
05448 }

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

04132 {
04133    struct queue_ent *ch;
04134    int res;
04135    int avl;
04136    int idx = 0;
04137    /* This needs a lock. How many members are available to be served? */
04138    ao2_lock(qe->parent);
04139 
04140    avl = num_available_members(qe->parent);
04141 
04142    ch = qe->parent->head;
04143 
04144    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
04145 
04146    while ((idx < avl) && (ch) && (ch != qe)) {
04147       if (!ch->pending)
04148          idx++;
04149       ch = ch->next;       
04150    }
04151 
04152    ao2_unlock(qe->parent);
04153    /* If the queue entry is within avl [the number of available members] calls from the top ... 
04154     * Autofill and position check added to support autofill=no (as only calls
04155     * from the front of the queue are valid when autofill is disabled)
04156     */
04157    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
04158       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
04159       res = 1;
04160    } else {
04161       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
04162       res = 0;
04163    }
04164 
04165    return res;
04166 }

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

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

02548 {
02549    struct call_queue *q;
02550    struct queue_ent *cur, *prev = NULL;
02551    int res = -1;
02552    int pos = 0;
02553    int inserted = 0;
02554 
02555    if (!(q = load_realtime_queue(queuename)))
02556       return res;
02557 
02558    ao2_lock(q);
02559 
02560    /* This is our one */
02561    if (q->joinempty) {
02562       int status = 0;
02563       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
02564          *reason = QUEUE_JOINEMPTY;
02565          ao2_unlock(q);
02566          queue_t_unref(q, "Done with realtime queue");
02567          return res;
02568       }
02569    }
02570    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02571       *reason = QUEUE_FULL;
02572    else if (*reason == QUEUE_UNKNOWN) {
02573       /* There's space for us, put us at the right position inside
02574        * the queue.
02575        * Take into account the priority of the calling user */
02576       inserted = 0;
02577       prev = NULL;
02578       cur = q->head;
02579       while (cur) {
02580          /* We have higher priority than the current user, enter
02581           * before him, after all the other users with priority
02582           * higher or equal to our priority. */
02583          if ((!inserted) && (qe->prio > cur->prio)) {
02584             insert_entry(q, prev, qe, &pos);
02585             inserted = 1;
02586          }
02587          /* <= is necessary for the position comparison because it may not be possible to enter
02588           * at our desired position since higher-priority callers may have taken the position we want
02589           */
02590          if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02591             insert_entry(q, prev, qe, &pos);
02592             inserted = 1;
02593             /*pos is incremented inside insert_entry, so don't need to add 1 here*/
02594             if (position < pos) {
02595                ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02596             }
02597          }
02598          cur->pos = ++pos;
02599          prev = cur;
02600          cur = cur->next;
02601       }
02602       /* No luck, join at the end of the queue */
02603       if (!inserted)
02604          insert_entry(q, prev, qe, &pos);
02605       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02606       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02607       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02608       q->count++;
02609       res = 0;
02610       ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02611          "Channel: %s\r\n"
02612          "CallerIDNum: %s\r\n"
02613          "CallerIDName: %s\r\n"
02614          "ConnectedLineNum: %s\r\n"
02615          "ConnectedLineName: %s\r\n"
02616          "Queue: %s\r\n"
02617          "Position: %d\r\n"
02618          "Count: %d\r\n"
02619          "Uniqueid: %s\r\n",
02620          qe->chan->name,
02621          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02622          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02623          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02624          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
02625          q->name, qe->pos, q->count, qe->chan->uniqueid );
02626       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02627    }
02628    ao2_unlock(q);
02629    queue_t_unref(q, "Done with realtime queue");
02630 
02631    return res;
02632 }

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

Definition at line 6961 of file app_queue.c.

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

Referenced by reload_single_queue().

06962 {
06963    struct member *member = obj;
06964 
06965    if (!member->delme) {
06966       member->status = get_queue_member_status(member);
06967       return 0;
06968    } else {
06969       return CMP_MATCH;
06970    }
06971 }

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

Definition at line 7105 of file app_queue.c.

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

Referenced by reload_queues().

07106 {
07107    struct call_queue *q = obj;
07108    char *queuename = arg;
07109    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
07110       return CMP_MATCH;
07111    } else {
07112       return 0;
07113    }
07114 }

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

02860 {
02861    struct call_queue *q;
02862    struct queue_ent *current, *prev = NULL;
02863    struct penalty_rule *pr_iter;
02864    int pos = 0;
02865 
02866    if (!(q = qe->parent))
02867       return;
02868    queue_t_ref(q, "Copy queue pointer from queue entry");
02869    ao2_lock(q);
02870 
02871    prev = NULL;
02872    for (current = q->head; current; current = current->next) {
02873       if (current == qe) {
02874          char posstr[20];
02875          q->count--;
02876 
02877          /* Take us out of the queue */
02878          ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
02879             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
02880             qe->chan->name, q->name,  q->count, qe->pos, qe->chan->uniqueid);
02881          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02882          /* Take us out of the queue */
02883          if (prev)
02884             prev->next = current->next;
02885          else
02886             q->head = current->next;
02887          /* Free penalty rules */
02888          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02889             ast_free(pr_iter);
02890          snprintf(posstr, sizeof(posstr), "%d", qe->pos);
02891          pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
02892       } else {
02893          /* Renumber the people after us in the queue based on a new count */
02894          current->pos = ++pos;
02895          prev = current;
02896       }
02897    }
02898    ao2_unlock(q);
02899 
02900    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02901    if (q->realtime) {
02902       struct ast_variable *var;
02903       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02904          q->dead = 1;
02905       } else {
02906          ast_variables_destroy(var);
02907       }
02908    }
02909 
02910    if (q->dead) { 
02911       /* It's dead and nobody is in it, so kill it */
02912       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02913    }
02914    /* unref the explicit ref earlier in the function */
02915    queue_t_unref(q, "Expire copied reference");
02916 }

static int load_module ( void   )  [static]

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

08783 {
08784    int res;
08785    struct ast_context *con;
08786    struct ast_flags mask = {AST_FLAGS_ALL, };
08787 
08788    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08789 
08790    use_weight = 0;
08791 
08792    if (reload_handler(0, &mask, NULL))
08793       return AST_MODULE_LOAD_DECLINE;
08794 
08795    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08796    if (!con)
08797       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08798    else
08799       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08800 
08801    if (queue_persistent_members)
08802       reload_queue_members();
08803 
08804    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08805 
08806    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08807    res = ast_register_application_xml(app, queue_exec);
08808    res |= ast_register_application_xml(app_aqm, aqm_exec);
08809    res |= ast_register_application_xml(app_rqm, rqm_exec);
08810    res |= ast_register_application_xml(app_pqm, pqm_exec);
08811    res |= ast_register_application_xml(app_upqm, upqm_exec);
08812    res |= ast_register_application_xml(app_ql, ql_exec);
08813    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08814    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08815    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08816    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08817    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08818    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08819    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08820    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08821    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08822    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08823    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08824    res |= ast_custom_function_register(&queuevar_function);
08825    res |= ast_custom_function_register(&queueexists_function);
08826    res |= ast_custom_function_register(&queuemembercount_function);
08827    res |= ast_custom_function_register(&queuemembercount_dep);
08828    res |= ast_custom_function_register(&queuememberlist_function);
08829    res |= ast_custom_function_register(&queuewaitingcount_function);
08830    res |= ast_custom_function_register(&queuememberpenalty_function);
08831 
08832    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08833       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08834    }
08835 
08836    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08837    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08838       res = -1;
08839    }
08840 
08841    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08842 
08843    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08844 
08845    return res ? AST_MODULE_LOAD_DECLINE : 0;
08846 }

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

02421 {
02422    struct ast_variable *queue_vars;
02423    struct ast_config *member_config = NULL;
02424    struct call_queue *q = NULL, tmpq = {
02425       .name = queuename,   
02426    };
02427    int prev_weight = 0;
02428 
02429    /* Find the queue in the in-core list first. */
02430    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02431 
02432    if (!q || q->realtime) {
02433       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
02434          queue operations while waiting for the DB.
02435 
02436          This will be two separate database transactions, so we might
02437          see queue parameters as they were before another process
02438          changed the queue and member list as it was after the change.
02439          Thus we might see an empty member list when a queue is
02440          deleted. In practise, this is unlikely to cause a problem. */
02441 
02442       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02443       if (queue_vars) {
02444          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02445          if (!member_config) {
02446             ast_debug(1, "No queue_members defined in config extconfig.conf\n");
02447             member_config = ast_config_new();
02448          }
02449       }
02450       if (q) {
02451          prev_weight = q->weight ? 1 : 0;
02452          queue_t_unref(q, "Need to find realtime queue");
02453       }
02454 
02455       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02456       ast_config_destroy(member_config);
02457       ast_variables_destroy(queue_vars);
02458 
02459       /* update the use_weight value if the queue's has gained or lost a weight */
02460       if (q) {
02461          if (!q->weight && prev_weight) {
02462             ast_atomic_fetchadd_int(&use_weight, -1);
02463          }
02464          if (q->weight && !prev_weight) {
02465             ast_atomic_fetchadd_int(&use_weight, +1);
02466          }
02467       }
02468       /* Other cases will end up with the proper value for use_weight */
02469    } else {
02470       update_realtime_members(q);
02471    }
02472    return q;
02473 }

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

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

07744 {
07745    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07746    int paused, penalty = 0;
07747 
07748    queuename = astman_get_header(m, "Queue");
07749    interface = astman_get_header(m, "Interface");
07750    penalty_s = astman_get_header(m, "Penalty");
07751    paused_s = astman_get_header(m, "Paused");
07752    membername = astman_get_header(m, "MemberName");
07753    state_interface = astman_get_header(m, "StateInterface");
07754 
07755    if (ast_strlen_zero(queuename)) {
07756       astman_send_error(s, m, "'Queue' not specified.");
07757       return 0;
07758    }
07759 
07760    if (ast_strlen_zero(interface)) {
07761       astman_send_error(s, m, "'Interface' not specified.");
07762       return 0;
07763    }
07764 
07765    if (ast_strlen_zero(penalty_s))
07766       penalty = 0;
07767    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07768       penalty = 0;
07769 
07770    if (ast_strlen_zero(paused_s))
07771       paused = 0;
07772    else
07773       paused = abs(ast_true(paused_s));
07774 
07775    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07776    case RES_OKAY:
07777       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07778       astman_send_ack(s, m, "Added interface to queue");
07779       break;
07780    case RES_EXISTS:
07781       astman_send_error(s, m, "Unable to add interface: Already there");
07782       break;
07783    case RES_NOSUCHQUEUE:
07784       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07785       break;
07786    case RES_OUTOFMEMORY:
07787       astman_send_error(s, m, "Out of memory");
07788       break;
07789    }
07790 
07791    return 0;
07792 }

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

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

07829 {
07830    const char *queuename, *interface, *paused_s, *reason;
07831    int paused;
07832 
07833    interface = astman_get_header(m, "Interface");
07834    paused_s = astman_get_header(m, "Paused");
07835    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07836    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07837 
07838    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07839       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07840       return 0;
07841    }
07842 
07843    paused = abs(ast_true(paused_s));
07844 
07845    if (set_member_paused(queuename, interface, reason, paused))
07846       astman_send_error(s, m, "Interface not found");
07847    else
07848       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07849    return 0;
07850 }

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

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

07853 {
07854    const char *queuename, *event, *message, *interface, *uniqueid;
07855 
07856    queuename = astman_get_header(m, "Queue");
07857    uniqueid = astman_get_header(m, "UniqueId");
07858    interface = astman_get_header(m, "Interface");
07859    event = astman_get_header(m, "Event");
07860    message = astman_get_header(m, "Message");
07861 
07862    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07863       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07864       return 0;
07865    }
07866 
07867    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07868    astman_send_ack(s, m, "Event added successfully");
07869 
07870    return 0;
07871 }

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

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

07952 {
07953    const char *queuename, *interface, *penalty_s;
07954    int penalty;
07955 
07956    interface = astman_get_header(m, "Interface");
07957    penalty_s = astman_get_header(m, "Penalty");
07958    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
07959    queuename = astman_get_header(m, "Queue");
07960 
07961    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07962       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07963       return 0;
07964    }
07965  
07966    penalty = atoi(penalty_s);
07967 
07968    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07969       astman_send_error(s, m, "Invalid interface, queuename or penalty");
07970    else
07971       astman_send_ack(s, m, "Interface penalty set successfully");
07972 
07973    return 0;
07974 }

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

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

07874 {
07875    struct ast_flags mask = {0,};
07876    const char *queuename = NULL;
07877    int header_found = 0;
07878 
07879    queuename = astman_get_header(m, "Queue");
07880    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07881       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07882       header_found = 1;
07883    }
07884    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07885       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07886       header_found = 1;
07887    }
07888    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07889       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07890       header_found = 1;
07891    }
07892 
07893    if (!header_found) {
07894       ast_set_flag(&mask, AST_FLAGS_ALL);
07895    }
07896 
07897    if (!reload_handler(1, &mask, queuename)) {
07898       astman_send_ack(s, m, "Queue reloaded successfully");
07899    } else {
07900       astman_send_error(s, m, "Error encountered while reloading queue");
07901    }
07902    return 0;
07903 }

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

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

07906 {
07907    const char *queuename = NULL;
07908    struct ast_flags mask = {QUEUE_RESET_STATS,};
07909    
07910    queuename = astman_get_header(m, "Queue");
07911 
07912    if (!reload_handler(1, &mask, queuename)) {
07913       astman_send_ack(s, m, "Queue stats reset successfully");
07914    } else {
07915       astman_send_error(s, m, "Error encountered while resetting queue stats");
07916    }
07917    return 0;
07918 }

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

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

07533 {
07534    const char *rule = astman_get_header(m, "Rule");
07535    const char *id = astman_get_header(m, "ActionID");
07536    struct rule_list *rl_iter;
07537    struct penalty_rule *pr_iter;
07538 
07539    astman_append(s, "Response: Success\r\n");
07540    if (!ast_strlen_zero(id)) {
07541       astman_append(s, "ActionID: %s\r\n", id);
07542    }
07543 
07544    AST_LIST_LOCK(&rule_lists);
07545    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07546       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07547          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07548          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07549             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 );
07550          }
07551          if (!ast_strlen_zero(rule))
07552             break;
07553       }
07554    }
07555    AST_LIST_UNLOCK(&rule_lists);
07556 
07557    /*
07558     * Two blank lines instead of one because the Response and
07559     * ActionID headers used to not be present.
07560     */
07561    astman_append(s, "\r\n\r\n");
07562 
07563    return RESULT_SUCCESS;
07564 }

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

Definition at line 7522 of file app_queue.c.

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

Referenced by load_module().

07523 {
07524    static const char * const a[] = { "queue", "show" };
07525 
07526    __queues_show(s, -1, 2, a);
07527    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07528 
07529    return RESULT_SUCCESS;
07530 }

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

Queue status info via AMI.

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

07643 {
07644    time_t now;
07645    int pos;
07646    const char *id = astman_get_header(m,"ActionID");
07647    const char *queuefilter = astman_get_header(m,"Queue");
07648    const char *memberfilter = astman_get_header(m,"Member");
07649    char idText[256] = "";
07650    struct call_queue *q;
07651    struct queue_ent *qe;
07652    float sl = 0;
07653    struct member *mem;
07654    struct ao2_iterator queue_iter;
07655    struct ao2_iterator mem_iter;
07656 
07657    astman_send_ack(s, m, "Queue status will follow");
07658    time(&now);
07659    if (!ast_strlen_zero(id))
07660       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07661 
07662    queue_iter = ao2_iterator_init(queues, 0);
07663    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07664       ao2_lock(q);
07665 
07666       /* List queue properties */
07667       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07668          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07669          astman_append(s, "Event: QueueParams\r\n"
07670             "Queue: %s\r\n"
07671             "Max: %d\r\n"
07672             "Strategy: %s\r\n"
07673             "Calls: %d\r\n"
07674             "Holdtime: %d\r\n"
07675             "TalkTime: %d\r\n"
07676             "Completed: %d\r\n"
07677             "Abandoned: %d\r\n"
07678             "ServiceLevel: %d\r\n"
07679             "ServicelevelPerf: %2.1f\r\n"
07680             "Weight: %d\r\n"
07681             "%s"
07682             "\r\n",
07683             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07684             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07685          /* List Queue Members */
07686          mem_iter = ao2_iterator_init(q->members, 0);
07687          while ((mem = ao2_iterator_next(&mem_iter))) {
07688             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07689                astman_append(s, "Event: QueueMember\r\n"
07690                   "Queue: %s\r\n"
07691                   "Name: %s\r\n"
07692                   "Location: %s\r\n"
07693                   "Membership: %s\r\n"
07694                   "Penalty: %d\r\n"
07695                   "CallsTaken: %d\r\n"
07696                   "LastCall: %d\r\n"
07697                   "Status: %d\r\n"
07698                   "Paused: %d\r\n"
07699                   "%s"
07700                   "\r\n",
07701                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07702                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07703             }
07704             ao2_ref(mem, -1);
07705          }
07706          ao2_iterator_destroy(&mem_iter);
07707          /* List Queue Entries */
07708          pos = 1;
07709          for (qe = q->head; qe; qe = qe->next) {
07710             astman_append(s, "Event: QueueEntry\r\n"
07711                "Queue: %s\r\n"
07712                "Position: %d\r\n"
07713                "Channel: %s\r\n"
07714                "Uniqueid: %s\r\n"
07715                "CallerIDNum: %s\r\n"
07716                "CallerIDName: %s\r\n"
07717                "ConnectedLineNum: %s\r\n"
07718                "ConnectedLineName: %s\r\n"
07719                "Wait: %ld\r\n"
07720                "%s"
07721                "\r\n",
07722                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07723                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07724                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07725                S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07726                S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07727                (long) (now - qe->start), idText);
07728          }
07729       }
07730       ao2_unlock(q);
07731       queue_t_unref(q, "Done with iterator");
07732    }
07733    ao2_iterator_destroy(&queue_iter);
07734 
07735    astman_append(s,
07736       "Event: QueueStatusComplete\r\n"
07737       "%s"
07738       "\r\n",idText);
07739 
07740    return RESULT_SUCCESS;
07741 }

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

Summary of queue info via the AMI.

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

07568 {
07569    time_t now;
07570    int qmemcount = 0;
07571    int qmemavail = 0;
07572    int qchancount = 0;
07573    int qlongestholdtime = 0;
07574    const char *id = astman_get_header(m, "ActionID");
07575    const char *queuefilter = astman_get_header(m, "Queue");
07576    char idText[256] = "";
07577    struct call_queue *q;
07578    struct queue_ent *qe;
07579    struct member *mem;
07580    struct ao2_iterator queue_iter;
07581    struct ao2_iterator mem_iter;
07582 
07583    astman_send_ack(s, m, "Queue summary will follow");
07584    time(&now);
07585    if (!ast_strlen_zero(id))
07586       snprintf(idText, 256, "ActionID: %s\r\n", id);
07587    queue_iter = ao2_iterator_init(queues, 0);
07588    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07589       ao2_lock(q);
07590 
07591       /* List queue properties */
07592       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07593          /* Reset the necessary local variables if no queuefilter is set*/
07594          qmemcount = 0;
07595          qmemavail = 0;
07596          qchancount = 0;
07597          qlongestholdtime = 0;
07598 
07599          /* List Queue Members */
07600          mem_iter = ao2_iterator_init(q->members, 0);
07601          while ((mem = ao2_iterator_next(&mem_iter))) {
07602             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07603                ++qmemcount;
07604                if (member_status_available(mem->status) && !mem->paused) {
07605                   ++qmemavail;
07606                }
07607             }
07608             ao2_ref(mem, -1);
07609          }
07610          ao2_iterator_destroy(&mem_iter);
07611          for (qe = q->head; qe; qe = qe->next) {
07612             if ((now - qe->start) > qlongestholdtime) {
07613                qlongestholdtime = now - qe->start;
07614             }
07615             ++qchancount;
07616          }
07617          astman_append(s, "Event: QueueSummary\r\n"
07618             "Queue: %s\r\n"
07619             "LoggedIn: %d\r\n"
07620             "Available: %d\r\n"
07621             "Callers: %d\r\n" 
07622             "HoldTime: %d\r\n"
07623             "TalkTime: %d\r\n"
07624             "LongestHoldTime: %d\r\n"
07625             "%s"
07626             "\r\n",
07627             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07628       }
07629       ao2_unlock(q);
07630       queue_t_unref(q, "Done with iterator");
07631    }
07632    ao2_iterator_destroy(&queue_iter);
07633    astman_append(s,
07634       "Event: QueueSummaryComplete\r\n"
07635       "%s"
07636       "\r\n", idText);
07637 
07638    return RESULT_SUCCESS;
07639 }

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

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

07795 {
07796    const char *queuename, *interface;
07797 
07798    queuename = astman_get_header(m, "Queue");
07799    interface = astman_get_header(m, "Interface");
07800 
07801    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07802       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07803       return 0;
07804    }
07805 
07806    switch (remove_from_queue(queuename, interface)) {
07807    case RES_OKAY:
07808       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07809       astman_send_ack(s, m, "Removed interface from queue");
07810       break;
07811    case RES_EXISTS:
07812       astman_send_error(s, m, "Unable to remove interface: Not there");
07813       break;
07814    case RES_NOSUCHQUEUE:
07815       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07816       break;
07817    case RES_OUTOFMEMORY:
07818       astman_send_error(s, m, "Out of memory");
07819       break;
07820    case RES_NOT_DYNAMIC:
07821       astman_send_error(s, m, "Member not dynamic");
07822       break;
07823    }
07824 
07825    return 0;
07826 }

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

Definition at line 7094 of file app_queue.c.

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

Referenced by reload_queues().

07095 {
07096    struct call_queue *q = obj;
07097    char *queuename = arg;
07098    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07099       q->dead = 1;
07100       q->found = 0;
07101    }
07102    return 0;
07103 }

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

Definition at line 6952 of file app_queue.c.

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

Referenced by reload_single_queue().

06953 {
06954    struct member *member = obj;
06955    if (!member->dynamic) {
06956       member->delme = 1;
06957    }
06958    return 0;
06959 }

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

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

02148 {
02149    ao2_lock(queue->members);
02150    mem->queuepos = ao2_container_count(queue->members);
02151    ao2_link(queue->members, mem);
02152    ao2_unlock(queue->members);
02153 }

static void member_call_pending_clear ( struct member mem  )  [static]

Definition at line 3108 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry(), and ring_entry().

03109 {
03110    ao2_lock(mem);
03111    mem->call_pending = 0;
03112    ao2_unlock(mem);
03113 }

static int member_call_pending_set ( struct member mem  )  [static]

Definition at line 3123 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry().

03124 {
03125    int old_pending;
03126 
03127    ao2_lock(mem);
03128    old_pending = mem->call_pending;
03129    mem->call_pending = 1;
03130    ao2_unlock(mem);
03131 
03132    return old_pending;
03133 }

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

Definition at line 1707 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01708 {
01709    struct member *mem1 = obj1, *mem2 = obj2;
01710    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01711 }

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

Definition at line 1695 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01696 {
01697    const struct member *mem = obj;
01698    const char *chname = strchr(mem->interface, '/');
01699    int ret = 0, i;
01700    if (!chname)
01701       chname = mem->interface;
01702    for (i = 0; i < 5 && chname[i]; i++)
01703       ret += compress_char(chname[i]) << (i * 6);
01704    return ret;
01705 }

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

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

02162 {
02163    ao2_lock(queue->members);
02164    queue_member_follower_removal(queue, mem);
02165    ao2_unlink(queue->members, mem);
02166    ao2_unlock(queue->members);
02167 }

static int member_status_available ( int  status  )  [static]

Definition at line 3095 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.

Referenced by can_ring_entry(), and manager_queues_summary().

03096 {
03097    return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN;
03098 }

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

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

Referenced by compare_weight(), and is_our_turn().

02965 {
02966    struct member *mem;
02967    int avl = 0;
02968    struct ao2_iterator mem_iter;
02969 
02970    mem_iter = ao2_iterator_init(q->members, 0);
02971    while ((mem = ao2_iterator_next(&mem_iter))) {
02972       switch (mem->status) {
02973       case AST_DEVICE_INUSE:
02974          if (!q->ringinuse)
02975             break;
02976          /* else fall through */
02977       case AST_DEVICE_NOT_INUSE:
02978       case AST_DEVICE_UNKNOWN:
02979          if (!mem->paused) {
02980             avl++;
02981          }
02982          break;
02983       }
02984       ao2_ref(mem, -1);
02985 
02986       /* If autofill is not enabled or if the queue's strategy is ringall, then
02987        * we really don't care about the number of available members so much as we
02988        * do that there is at least one available.
02989        *
02990        * In fact, we purposely will return from this function stating that only
02991        * one member is available if either of those conditions hold. That way,
02992        * functions which determine what action to take based on the number of available
02993        * members will operate properly. The reasoning is that even if multiple
02994        * members are available, only the head caller can actually be serviced.
02995        */
02996       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02997          break;
02998       }
02999    }
03000    ao2_iterator_destroy(&mem_iter);
03001 
03002    return avl;
03003 }

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

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

01902 {
01903    char *value_copy = ast_strdupa(value);
01904    char *option = NULL;
01905    while ((option = strsep(&value_copy, ","))) {
01906       if (!strcasecmp(option, "paused")) {
01907          *empty |= QUEUE_EMPTY_PAUSED;
01908       } else if (!strcasecmp(option, "penalty")) {
01909          *empty |= QUEUE_EMPTY_PENALTY;
01910       } else if (!strcasecmp(option, "inuse")) {
01911          *empty |= QUEUE_EMPTY_INUSE;
01912       } else if (!strcasecmp(option, "ringing")) {
01913          *empty |= QUEUE_EMPTY_RINGING;
01914       } else if (!strcasecmp(option, "invalid")) {
01915          *empty |= QUEUE_EMPTY_INVALID;
01916       } else if (!strcasecmp(option, "wrapup")) {
01917          *empty |= QUEUE_EMPTY_WRAPUP;
01918       } else if (!strcasecmp(option, "unavailable")) {
01919          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01920       } else if (!strcasecmp(option, "unknown")) {
01921          *empty |= QUEUE_EMPTY_UNKNOWN;
01922       } else if (!strcasecmp(option, "loose")) {
01923          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01924       } else if (!strcasecmp(option, "strict")) {
01925          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01926       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01927          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01928       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01929          *empty = 0;
01930       } else {
01931          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01932       }
01933    }
01934 }

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

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

02635 {
02636    int res;
02637 
02638    if (ast_strlen_zero(filename)) {
02639       return 0;
02640    }
02641 
02642    if (!ast_fileexists(filename, NULL, chan->language)) {
02643       return 0;
02644    }
02645 
02646    ast_stopstream(chan);
02647 
02648    res = ast_streamfile(chan, filename, chan->language);
02649    if (!res)
02650       res = ast_waitstream(chan, AST_DIGIT_ANY);
02651 
02652    ast_stopstream(chan);
02653 
02654    return res;
02655 }

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

PauseQueueMember application.

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

05860 {
05861    char *parse;
05862    AST_DECLARE_APP_ARGS(args,
05863       AST_APP_ARG(queuename);
05864       AST_APP_ARG(interface);
05865       AST_APP_ARG(options);
05866       AST_APP_ARG(reason);
05867    );
05868 
05869    if (ast_strlen_zero(data)) {
05870       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05871       return -1;
05872    }
05873 
05874    parse = ast_strdupa(data);
05875 
05876    AST_STANDARD_APP_ARGS(args, parse);
05877 
05878    if (ast_strlen_zero(args.interface)) {
05879       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05880       return -1;
05881    }
05882 
05883    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05884       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05885       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05886       return 0;
05887    }
05888 
05889    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05890 
05891    return 0;
05892 }

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

QueueLog application.

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

06051 {
06052    char *parse;
06053 
06054    AST_DECLARE_APP_ARGS(args,
06055       AST_APP_ARG(queuename);
06056       AST_APP_ARG(uniqueid);
06057       AST_APP_ARG(membername);
06058       AST_APP_ARG(event);
06059       AST_APP_ARG(params);
06060    );
06061 
06062    if (ast_strlen_zero(data)) {
06063       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
06064       return -1;
06065    }
06066 
06067    parse = ast_strdupa(data);
06068 
06069    AST_STANDARD_APP_ARGS(args, parse);
06070 
06071    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
06072        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
06073       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
06074       return -1;
06075    }
06076 
06077    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
06078       "%s", args.params ? args.params : "");
06079 
06080    return 0;
06081 }

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

Definition at line 1260 of file app_queue.c.

References CMP_MATCH, and CMP_STOP.

Referenced by load_module().

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

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

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

01290 {
01291    struct member *mem = obj;
01292    struct call_queue *queue = arg;
01293    int rrpos = mem->queuepos;
01294 
01295    if (mem->delme) {
01296       ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &rrpos);
01297    }
01298 
01299    return 0;
01300 }

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

06125 {
06126    int res=-1;
06127    int ringing=0;
06128    const char *user_priority;
06129    const char *max_penalty_str;
06130    const char *min_penalty_str;
06131    int prio;
06132    int qcontinue = 0;
06133    int max_penalty, min_penalty;
06134    enum queue_result reason = QUEUE_UNKNOWN;
06135    /* whether to exit Queue application after the timeout hits */
06136    int tries = 0;
06137    int noption = 0;
06138    char *parse;
06139    int makeannouncement = 0;
06140    int position = 0;
06141    AST_DECLARE_APP_ARGS(args,
06142       AST_APP_ARG(queuename);
06143       AST_APP_ARG(options);
06144       AST_APP_ARG(url);
06145       AST_APP_ARG(announceoverride);
06146       AST_APP_ARG(queuetimeoutstr);
06147       AST_APP_ARG(agi);
06148       AST_APP_ARG(macro);
06149       AST_APP_ARG(gosub);
06150       AST_APP_ARG(rule);
06151       AST_APP_ARG(position);
06152    );
06153    /* Our queue entry */
06154    struct queue_ent qe = { 0 };
06155    
06156    if (ast_strlen_zero(data)) {
06157       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
06158       return -1;
06159    }
06160    
06161    parse = ast_strdupa(data);
06162    AST_STANDARD_APP_ARGS(args, parse);
06163 
06164    /* Setup our queue entry */
06165    qe.start = time(NULL);
06166 
06167    /* set the expire time based on the supplied timeout; */
06168    if (!ast_strlen_zero(args.queuetimeoutstr))
06169       qe.expire = qe.start + atoi(args.queuetimeoutstr);
06170    else
06171       qe.expire = 0;
06172 
06173    /* Get the priority from the variable ${QUEUE_PRIO} */
06174    ast_channel_lock(chan);
06175    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
06176    if (user_priority) {
06177       if (sscanf(user_priority, "%30d", &prio) == 1) {
06178          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
06179       } else {
06180          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
06181             user_priority, chan->name);
06182          prio = 0;
06183       }
06184    } else {
06185       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
06186       prio = 0;
06187    }
06188 
06189    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
06190 
06191    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
06192       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
06193          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
06194       } else {
06195          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
06196             max_penalty_str, chan->name);
06197          max_penalty = INT_MAX;
06198       }
06199    } else {
06200       max_penalty = INT_MAX;
06201    }
06202 
06203    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
06204       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
06205          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
06206       } else {
06207          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
06208             min_penalty_str, chan->name);
06209          min_penalty = INT_MAX;
06210       }
06211    } else {
06212       min_penalty = INT_MAX;
06213    }
06214    ast_channel_unlock(chan);
06215 
06216    if (args.options && (strchr(args.options, 'r')))
06217       ringing = 1;
06218 
06219    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
06220       qe.ring_when_ringing = 1;
06221    }
06222 
06223    if (args.options && (strchr(args.options, 'c')))
06224       qcontinue = 1;
06225 
06226    if (args.position) {
06227       position = atoi(args.position);
06228       if (position < 0) {
06229          ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
06230          position = 0;
06231       }
06232    }
06233 
06234    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
06235       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
06236 
06237    qe.chan = chan;
06238    qe.prio = prio;
06239    qe.max_penalty = max_penalty;
06240    qe.min_penalty = min_penalty;
06241    qe.last_pos_said = 0;
06242    qe.last_pos = 0;
06243    qe.last_periodic_announce_time = time(NULL);
06244    qe.last_periodic_announce_sound = 0;
06245    qe.valid_digits = 0;
06246    if (join_queue(args.queuename, &qe, &reason, position)) {
06247       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
06248       set_queue_result(chan, reason);
06249       return 0;
06250    }
06251    ast_assert(qe.parent != NULL);
06252 
06253    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
06254       S_OR(args.url, ""),
06255       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
06256       qe.opos);
06257    copy_rules(&qe, args.rule);
06258    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
06259 check_turns:
06260    if (ringing) {
06261       ast_indicate(chan, AST_CONTROL_RINGING);
06262    } else {
06263       ast_moh_start(chan, qe.moh, NULL);
06264    }
06265 
06266    /* This is the wait loop for callers 2 through maxlen */
06267    res = wait_our_turn(&qe, ringing, &reason);
06268    if (res) {
06269       goto stop;
06270    }
06271 
06272    makeannouncement = 0;
06273 
06274    for (;;) {
06275       /* This is the wait loop for the head caller*/
06276       /* To exit, they may get their call answered; */
06277       /* they may dial a digit from the queue context; */
06278       /* or, they may timeout. */
06279 
06280       /* Leave if we have exceeded our queuetimeout */
06281       if (qe.expire && (time(NULL) >= qe.expire)) {
06282          record_abandoned(&qe);
06283          reason = QUEUE_TIMEOUT;
06284          res = 0;
06285          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
06286             qe.pos, qe.opos, (long) time(NULL) - qe.start);
06287          break;
06288       }
06289 
06290       if (makeannouncement) {
06291          /* Make a position announcement, if enabled */
06292          if (qe.parent->announcefrequency)
06293             if ((res = say_position(&qe,ringing)))
06294                goto stop;
06295       }
06296       makeannouncement = 1;
06297 
06298       /* Make a periodic announcement, if enabled */
06299       if (qe.parent->periodicannouncefrequency)
06300          if ((res = say_periodic_announcement(&qe,ringing)))
06301             goto stop;
06302    
06303       /* Leave if we have exceeded our queuetimeout */
06304       if (qe.expire && (time(NULL) >= qe.expire)) {
06305          record_abandoned(&qe);
06306          reason = QUEUE_TIMEOUT;
06307          res = 0;
06308          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06309          break;
06310       }
06311 
06312       /* see if we need to move to the next penalty level for this queue */
06313       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
06314          update_qe_rule(&qe);
06315       }
06316 
06317       /* Try calling all queue members for 'timeout' seconds */
06318       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
06319       if (res) {
06320          goto stop;
06321       }
06322 
06323       if (qe.parent->leavewhenempty) {
06324          int status = 0;
06325          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
06326             record_abandoned(&qe);
06327             reason = QUEUE_LEAVEEMPTY;
06328             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
06329             res = 0;
06330             break;
06331          }
06332       }
06333 
06334       /* exit after 'timeout' cycle if 'n' option enabled */
06335       if (noption && tries >= ao2_container_count(qe.parent->members)) {
06336          ast_verb(3, "Exiting on time-out cycle\n");
06337          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06338          record_abandoned(&qe);
06339          reason = QUEUE_TIMEOUT;
06340          res = 0;
06341          break;
06342       }
06343 
06344       
06345       /* Leave if we have exceeded our queuetimeout */
06346       if (qe.expire && (time(NULL) >= qe.expire)) {
06347          record_abandoned(&qe);
06348          reason = QUEUE_TIMEOUT;
06349          res = 0;
06350          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06351          break;
06352       }
06353 
06354       /* If using dynamic realtime members, we should regenerate the member list for this queue */
06355       update_realtime_members(qe.parent);
06356       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
06357       res = wait_a_bit(&qe);
06358       if (res)
06359          goto stop;
06360 
06361       /* Since this is a priority queue and
06362        * it is not sure that we are still at the head
06363        * of the queue, go and check for our turn again.
06364        */
06365       if (!is_our_turn(&qe)) {
06366          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
06367          goto check_turns;
06368       }
06369    }
06370 
06371 stop:
06372    if (res) {
06373       if (res < 0) {
06374          if (!qe.handled) {
06375             record_abandoned(&qe);
06376             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
06377                "%d|%d|%ld", qe.pos, qe.opos,
06378                (long) time(NULL) - qe.start);
06379             res = -1;
06380          } else if (qcontinue) {
06381             reason = QUEUE_CONTINUE;
06382             res = 0;
06383          }
06384       } else if (qe.valid_digits) {
06385          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
06386             "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) time(NULL) - qe.start);
06387       }
06388    }
06389 
06390    /* Don't allow return code > 0 */
06391    if (res >= 0) {
06392       res = 0; 
06393       if (ringing) {
06394          ast_indicate(chan, -1);
06395       } else {
06396          ast_moh_stop(chan);
06397       }        
06398       ast_stopstream(chan);
06399    }
06400 
06401    set_queue_variables(qe.parent, qe.chan);
06402 
06403    leave_queue(&qe);
06404    if (reason != QUEUE_UNKNOWN)
06405       set_queue_result(chan, reason);
06406 
06407    /*
06408     * every queue_ent is given a reference to it's parent
06409     * call_queue when it joins the queue.  This ref must be taken
06410     * away right before the queue_ent is destroyed.  In this case
06411     * the queue_ent is about to be returned on the stack
06412     */
06413    qe.parent = queue_unref(qe.parent);
06414 
06415    return res;
06416 }

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

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

06471 {
06472    struct call_queue *q;
06473 
06474    buf[0] = '\0';
06475 
06476    if (ast_strlen_zero(data)) {
06477       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06478       return -1;
06479    }
06480    q = load_realtime_queue(data);
06481    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06482    if (q) {
06483       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06484    }
06485 
06486    return 0;
06487 }

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

06690 {
06691    int penalty;
06692    AST_DECLARE_APP_ARGS(args,
06693       AST_APP_ARG(queuename);
06694       AST_APP_ARG(interface);
06695    );
06696    /* Make sure the returned value on error is NULL. */
06697    buf[0] = '\0';
06698 
06699    if (ast_strlen_zero(data)) {
06700       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06701       return -1;
06702    }
06703 
06704    AST_STANDARD_APP_ARGS(args, data);
06705 
06706    if (args.argc < 2) {
06707       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06708       return -1;
06709    }
06710 
06711    penalty = get_member_penalty (args.queuename, args.interface);
06712    
06713    if (penalty >= 0) /* remember that buf is already '\0' */
06714       snprintf (buf, len, "%d", penalty);
06715 
06716    return 0;
06717 }

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

06721 {
06722    int penalty;
06723    AST_DECLARE_APP_ARGS(args,
06724       AST_APP_ARG(queuename);
06725       AST_APP_ARG(interface);
06726    );
06727 
06728    if (ast_strlen_zero(data)) {
06729       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06730       return -1;
06731    }
06732 
06733    AST_STANDARD_APP_ARGS(args, data);
06734 
06735    if (args.argc < 2) {
06736       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06737       return -1;
06738    }
06739 
06740    penalty = atoi(value);
06741 
06742    if (ast_strlen_zero(args.interface)) {
06743       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06744       return -1;
06745    }
06746 
06747    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06748    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06749       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06750       return -1;
06751    }
06752 
06753    return 0;
06754 }

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

06495 {
06496    int count = 0;
06497    struct member *m;
06498    struct ao2_iterator mem_iter;
06499    struct call_queue *q;
06500    char *option;
06501 
06502    if (ast_strlen_zero(data)) {
06503       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06504       return -1;
06505    }
06506 
06507    if ((option = strchr(data, ',')))
06508       *option++ = '\0';
06509    else
06510       option = "logged";
06511    if ((q = load_realtime_queue(data))) {
06512       ao2_lock(q);
06513       if (!strcasecmp(option, "logged")) {
06514          mem_iter = ao2_iterator_init(q->members, 0);
06515          while ((m = ao2_iterator_next(&mem_iter))) {
06516             /* Count the agents who are logged in and presently answering calls */
06517             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06518                count++;
06519             }
06520             ao2_ref(m, -1);
06521          }
06522          ao2_iterator_destroy(&mem_iter);
06523       } else if (!strcasecmp(option, "free")) {
06524          mem_iter = ao2_iterator_init(q->members, 0);
06525          while ((m = ao2_iterator_next(&mem_iter))) {
06526             /* Count the agents who are logged in and presently answering calls */
06527             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06528                count++;
06529             }
06530             ao2_ref(m, -1);
06531          }
06532          ao2_iterator_destroy(&mem_iter);
06533       } else if (!strcasecmp(option, "ready")) {
06534          time_t now;
06535          time(&now);
06536          mem_iter = ao2_iterator_init(q->members, 0);
06537          while ((m = ao2_iterator_next(&mem_iter))) {
06538             /* Count the agents who are logged in, not paused and not wrapping up */
06539             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06540                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06541                count++;
06542             }
06543             ao2_ref(m, -1);
06544          }
06545          ao2_iterator_destroy(&mem_iter);
06546       } else /* must be "count" */
06547          count = ao2_container_count(q->members);
06548       ao2_unlock(q);
06549       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06550    } else
06551       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06552 
06553    snprintf(buf, len, "%d", count);
06554 
06555    return 0;
06556 }

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

06564 {
06565    int count = 0;
06566    struct member *m;
06567    struct call_queue *q;
06568    struct ao2_iterator mem_iter;
06569    static int depflag = 1;
06570 
06571    if (depflag) {
06572       depflag = 0;
06573       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");
06574    }
06575 
06576    if (ast_strlen_zero(data)) {
06577       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06578       return -1;
06579    }
06580    
06581    if ((q = load_realtime_queue(data))) {
06582       ao2_lock(q);
06583       mem_iter = ao2_iterator_init(q->members, 0);
06584       while ((m = ao2_iterator_next(&mem_iter))) {
06585          /* Count the agents who are logged in and presently answering calls */
06586          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06587             count++;
06588          }
06589          ao2_ref(m, -1);
06590       }
06591       ao2_iterator_destroy(&mem_iter);
06592       ao2_unlock(q);
06593       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06594    } else
06595       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06596 
06597    snprintf(buf, len, "%d", count);
06598 
06599    return 0;
06600 }

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

06640 {
06641    struct call_queue *q, tmpq = {
06642       .name = data,  
06643    };
06644    struct member *m;
06645 
06646    /* Ensure an otherwise empty list doesn't return garbage */
06647    buf[0] = '\0';
06648 
06649    if (ast_strlen_zero(data)) {
06650       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06651       return -1;
06652    }
06653 
06654    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06655       int buflen = 0, count = 0;
06656       struct ao2_iterator mem_iter;
06657 
06658       ao2_lock(q);
06659       mem_iter = ao2_iterator_init(q->members, 0);
06660       while ((m = ao2_iterator_next(&mem_iter))) {
06661          /* strcat() is always faster than printf() */
06662          if (count++) {
06663             strncat(buf + buflen, ",", len - buflen - 1);
06664             buflen++;
06665          }
06666          strncat(buf + buflen, m->interface, len - buflen - 1);
06667          buflen += strlen(m->interface);
06668          /* Safeguard against overflow (negative length) */
06669          if (buflen >= len - 2) {
06670             ao2_ref(m, -1);
06671             ast_log(LOG_WARNING, "Truncating list\n");
06672             break;
06673          }
06674          ao2_ref(m, -1);
06675       }
06676       ao2_iterator_destroy(&mem_iter);
06677       ao2_unlock(q);
06678       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06679    } else
06680       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06681 
06682    /* We should already be terminated, but let's make sure. */
06683    buf[len - 1] = '\0';
06684 
06685    return 0;
06686 }

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

06604 {
06605    int count = 0;
06606    struct call_queue *q, tmpq = {
06607       .name = data,  
06608    };
06609    struct ast_variable *var = NULL;
06610 
06611    buf[0] = '\0';
06612    
06613    if (ast_strlen_zero(data)) {
06614       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06615       return -1;
06616    }
06617 
06618    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06619       ao2_lock(q);
06620       count = q->count;
06621       ao2_unlock(q);
06622       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06623    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06624       /* if the queue is realtime but was not found in memory, this
06625        * means that the queue had been deleted from memory since it was 
06626        * "dead." This means it has a 0 waiting count
06627        */
06628       count = 0;
06629       ast_variables_destroy(var);
06630    } else
06631       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06632 
06633    snprintf(buf, len, "%d", count);
06634 
06635    return 0;
06636 }

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

06424 {
06425    int res = -1;
06426    struct call_queue *q, tmpq = {
06427       .name = data,  
06428    };
06429 
06430    char interfacevar[256] = "";
06431    float sl = 0;
06432 
06433    if (ast_strlen_zero(data)) {
06434       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06435       return -1;
06436    }
06437 
06438    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06439       ao2_lock(q);
06440       if (q->setqueuevar) {
06441          sl = 0;
06442          res = 0;
06443 
06444          if (q->callscompleted > 0) {
06445             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06446          }
06447 
06448          snprintf(interfacevar, sizeof(interfacevar),
06449             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06450             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06451 
06452          pbx_builtin_setvar_multiple(chan, interfacevar);
06453       }
06454 
06455       ao2_unlock(q);
06456       queue_t_unref(q, "Done with QUEUE() function");
06457    } else {
06458       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06459    }
06460 
06461    snprintf(buf, len, "%d", res);
06462 
06463    return 0;
06464 }

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

Definition at line 1253 of file app_queue.c.

References ast_str_case_hash().

Referenced by load_module().

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

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

Definition at line 1271 of file app_queue.c.

References member::queuepos.

Referenced by queue_delme_members_decrement_followers(), and queue_member_follower_removal().

01272 {
01273    struct member *mem = obj;
01274    int *decrement_followers_after = arg;
01275 
01276    if (mem->queuepos > *decrement_followers_after) {
01277       mem->queuepos--;
01278    }
01279 
01280    return 0;
01281 }

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

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

01308 {
01309    int pos = mem->queuepos;
01310 
01311    /* 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
01312     * who would have been next otherwise. */
01313    if (pos < queue->rrpos) {
01314       queue->rrpos--;
01315    }
01316 
01317    ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &pos);
01318 }

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

Definition at line 1332 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01333 {
01334    ao2_ref(q, 1);
01335    return q;
01336 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06848 {
06849    const char *general_val = NULL;
06850    queue_persistent_members = 0;
06851    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06852       queue_persistent_members = ast_true(general_val);
06853    autofill_default = 0;
06854    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06855       autofill_default = ast_true(general_val);
06856    montype_default = 0;
06857    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06858       if (!strcasecmp(general_val, "mixmonitor"))
06859          montype_default = 1;
06860    }
06861    update_cdr = 0;
06862    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06863       update_cdr = ast_true(general_val);
06864    shared_lastcall = 0;
06865    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06866       shared_lastcall = ast_true(general_val);
06867 }

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 1944 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::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().

01945 {
01946    if (!strcasecmp(param, "musicclass") || 
01947       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01948       ast_string_field_set(q, moh, val);
01949    } else if (!strcasecmp(param, "announce")) {
01950       ast_string_field_set(q, announce, val);
01951    } else if (!strcasecmp(param, "context")) {
01952       ast_string_field_set(q, context, val);
01953    } else if (!strcasecmp(param, "timeout")) {
01954       q->timeout = atoi(val);
01955       if (q->timeout < 0)
01956          q->timeout = DEFAULT_TIMEOUT;
01957    } else if (!strcasecmp(param, "ringinuse")) {
01958       q->ringinuse = ast_true(val);
01959    } else if (!strcasecmp(param, "setinterfacevar")) {
01960       q->setinterfacevar = ast_true(val);
01961    } else if (!strcasecmp(param, "setqueuevar")) {
01962       q->setqueuevar = ast_true(val);
01963    } else if (!strcasecmp(param, "setqueueentryvar")) {
01964       q->setqueueentryvar = ast_true(val);
01965    } else if (!strcasecmp(param, "monitor-format")) {
01966       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01967    } else if (!strcasecmp(param, "membermacro")) {
01968       ast_string_field_set(q, membermacro, val);
01969    } else if (!strcasecmp(param, "membergosub")) {
01970       ast_string_field_set(q, membergosub, val);
01971    } else if (!strcasecmp(param, "queue-youarenext")) {
01972       ast_string_field_set(q, sound_next, val);
01973    } else if (!strcasecmp(param, "queue-thereare")) {
01974       ast_string_field_set(q, sound_thereare, val);
01975    } else if (!strcasecmp(param, "queue-callswaiting")) {
01976       ast_string_field_set(q, sound_calls, val);
01977    } else if (!strcasecmp(param, "queue-quantity1")) {
01978       ast_string_field_set(q, queue_quantity1, val);
01979    } else if (!strcasecmp(param, "queue-quantity2")) {
01980       ast_string_field_set(q, queue_quantity2, val);
01981    } else if (!strcasecmp(param, "queue-holdtime")) {
01982       ast_string_field_set(q, sound_holdtime, val);
01983    } else if (!strcasecmp(param, "queue-minutes")) {
01984       ast_string_field_set(q, sound_minutes, val);
01985    } else if (!strcasecmp(param, "queue-minute")) {
01986       ast_string_field_set(q, sound_minute, val);
01987    } else if (!strcasecmp(param, "queue-seconds")) {
01988       ast_string_field_set(q, sound_seconds, val);
01989    } else if (!strcasecmp(param, "queue-thankyou")) {
01990       ast_string_field_set(q, sound_thanks, val);
01991    } else if (!strcasecmp(param, "queue-callerannounce")) {
01992       ast_string_field_set(q, sound_callerannounce, val);
01993    } else if (!strcasecmp(param, "queue-reporthold")) {
01994       ast_string_field_set(q, sound_reporthold, val);
01995    } else if (!strcasecmp(param, "announce-frequency")) {
01996       q->announcefrequency = atoi(val);
01997    } else if (!strcasecmp(param, "announce-to-first-user")) {
01998       q->announce_to_first_user = ast_true(val);
01999    } else if (!strcasecmp(param, "min-announce-frequency")) {
02000       q->minannouncefrequency = atoi(val);
02001       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
02002    } else if (!strcasecmp(param, "announce-round-seconds")) {
02003       q->roundingseconds = atoi(val);
02004       /* Rounding to any other values just doesn't make sense... */
02005       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
02006          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
02007          if (linenum >= 0) {
02008             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
02009                "using 0 instead for queue '%s' at line %d of queues.conf\n",
02010                val, param, q->name, linenum);
02011          } else {
02012             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
02013                "using 0 instead for queue '%s'\n", val, param, q->name);
02014          }
02015          q->roundingseconds=0;
02016       }
02017    } else if (!strcasecmp(param, "announce-holdtime")) {
02018       if (!strcasecmp(val, "once"))
02019          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
02020       else if (ast_true(val))
02021          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
02022       else
02023          q->announceholdtime = 0;
02024    } else if (!strcasecmp(param, "announce-position")) {
02025       if (!strcasecmp(val, "limit"))
02026          q->announceposition = ANNOUNCEPOSITION_LIMIT;
02027       else if (!strcasecmp(val, "more"))
02028          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
02029       else if (ast_true(val))
02030          q->announceposition = ANNOUNCEPOSITION_YES;
02031       else
02032          q->announceposition = ANNOUNCEPOSITION_NO;
02033    } else if (!strcasecmp(param, "announce-position-limit")) {
02034       q->announcepositionlimit = atoi(val);
02035    } else if (!strcasecmp(param, "periodic-announce")) {
02036       if (strchr(val, ',')) {
02037          char *s, *buf = ast_strdupa(val);
02038          unsigned int i = 0;
02039 
02040          while ((s = strsep(&buf, ",|"))) {
02041             if (!q->sound_periodicannounce[i])
02042                q->sound_periodicannounce[i] = ast_str_create(16);
02043             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
02044             i++;
02045             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
02046                break;
02047          }
02048          q->numperiodicannounce = i;
02049       } else {
02050          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
02051          q->numperiodicannounce = 1;
02052       }
02053    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
02054       q->periodicannouncefrequency = atoi(val);
02055    } else if (!strcasecmp(param, "relative-periodic-announce")) {
02056       q->relativeperiodicannounce = ast_true(val);
02057    } else if (!strcasecmp(param, "random-periodic-announce")) {
02058       q->randomperiodicannounce = ast_true(val);
02059    } else if (!strcasecmp(param, "retry")) {
02060       q->retry = atoi(val);
02061       if (q->retry <= 0)
02062          q->retry = DEFAULT_RETRY;
02063    } else if (!strcasecmp(param, "wrapuptime")) {
02064       q->wrapuptime = atoi(val);
02065    } else if (!strcasecmp(param, "penaltymemberslimit")) {
02066       if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
02067          q->penaltymemberslimit = 0;
02068       }
02069    } else if (!strcasecmp(param, "autofill")) {
02070       q->autofill = ast_true(val);
02071    } else if (!strcasecmp(param, "monitor-type")) {
02072       if (!strcasecmp(val, "mixmonitor"))
02073          q->montype = 1;
02074    } else if (!strcasecmp(param, "autopause")) {
02075       q->autopause = autopause2int(val);
02076    } else if (!strcasecmp(param, "maxlen")) {
02077       q->maxlen = atoi(val);
02078       if (q->maxlen < 0)
02079          q->maxlen = 0;
02080    } else if (!strcasecmp(param, "servicelevel")) {
02081       q->servicelevel= atoi(val);
02082    } else if (!strcasecmp(param, "strategy")) {
02083       int strategy;
02084 
02085       /* We are a static queue and already have set this, no need to do it again */
02086       if (failunknown) {
02087          return;
02088       }
02089       strategy = strat2int(val);
02090       if (strategy < 0) {
02091          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02092             val, q->name);
02093          q->strategy = QUEUE_STRATEGY_RINGALL;
02094       }
02095       if (strategy == q->strategy) {
02096          return;
02097       }
02098       if (strategy == QUEUE_STRATEGY_LINEAR) {
02099          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
02100          return;
02101       }
02102       q->strategy = strategy;
02103    } else if (!strcasecmp(param, "joinempty")) {
02104       parse_empty_options(val, &q->joinempty, 1);
02105    } else if (!strcasecmp(param, "leavewhenempty")) {
02106       parse_empty_options(val, &q->leavewhenempty, 0);
02107    } else if (!strcasecmp(param, "eventmemberstatus")) {
02108       q->maskmemberstatus = !ast_true(val);
02109    } else if (!strcasecmp(param, "eventwhencalled")) {
02110       if (!strcasecmp(val, "vars")) {
02111          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
02112       } else {
02113          q->eventwhencalled = ast_true(val) ? 1 : 0;
02114       }
02115    } else if (!strcasecmp(param, "reportholdtime")) {
02116       q->reportholdtime = ast_true(val);
02117    } else if (!strcasecmp(param, "memberdelay")) {
02118       q->memberdelay = atoi(val);
02119    } else if (!strcasecmp(param, "weight")) {
02120       q->weight = atoi(val);
02121    } else if (!strcasecmp(param, "timeoutrestart")) {
02122       q->timeoutrestart = ast_true(val);
02123    } else if (!strcasecmp(param, "defaultrule")) {
02124       ast_string_field_set(q, defaultrule, val);
02125    } else if (!strcasecmp(param, "timeoutpriority")) {
02126       if (!strcasecmp(val, "conf")) {
02127          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
02128       } else {
02129          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
02130       }
02131    } else if (failunknown) {
02132       if (linenum >= 0) {
02133          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
02134             q->name, param, linenum);
02135       } else {
02136          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
02137       }
02138    }
02139 }

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

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

07504 {
07505    switch ( cmd ) {
07506    case CLI_INIT:
07507       e->command = "queue show";
07508       e->usage =
07509          "Usage: queue show\n"
07510          "       Provides summary information on a specified queue.\n";
07511       return NULL;
07512    case CLI_GENERATE:
07513       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07514    }
07515 
07516    return __queues_show(NULL, a->fd, a->argc, a->argv);
07517 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4481 of file app_queue.c.

References ast_free.

04482 {
04483    struct queue_transfer_ds *qtds = data;
04484    ast_free(qtds);
04485 }

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

04505 {
04506    struct queue_transfer_ds *qtds = data;
04507    struct queue_ent *qe = qtds->qe;
04508    struct member *member = qtds->member;
04509    time_t callstart = qtds->starttime;
04510    int callcompletedinsl = qtds->callcompletedinsl;
04511    struct ast_datastore *datastore;
04512 
04513    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04514             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04515             (long) (time(NULL) - callstart), qe->opos);
04516 
04517    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04518    
04519    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04520    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04521       ast_channel_datastore_remove(old_chan, datastore);
04522    } else {
04523       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04524    }
04525 }

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

Definition at line 1338 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec(), and queues_data_provider_get().

01339 {
01340    ao2_ref(q, -1);
01341    return NULL;
01342 }

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

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

08674 {
08675    struct ao2_iterator i;
08676    struct call_queue *queue, *queue_realtime = NULL;
08677    struct ast_config *cfg;
08678    char *queuename;
08679 
08680    /* load realtime queues. */
08681    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08682    if (cfg) {
08683       for (queuename = ast_category_browse(cfg, NULL);
08684             !ast_strlen_zero(queuename);
08685             queuename = ast_category_browse(cfg, queuename)) {
08686          if ((queue = load_realtime_queue(queuename))) {
08687             queue_unref(queue);
08688          }
08689       }
08690       ast_config_destroy(cfg);
08691    }
08692 
08693    /* static queues. */
08694    i = ao2_iterator_init(queues, 0);
08695    while ((queue = ao2_iterator_next(&i))) {
08696       ao2_lock(queue);
08697       if (queue->realtime) {
08698          queue_realtime = load_realtime_queue(queue->name);
08699          if (!queue_realtime) {
08700             ao2_unlock(queue);
08701             queue_unref(queue);
08702             continue;
08703          }
08704          queue_unref(queue_realtime);
08705       }
08706 
08707       queues_data_provider_get_helper(search, data_root, queue);
08708       ao2_unlock(queue);
08709       queue_unref(queue);
08710    }
08711    ao2_iterator_destroy(&i);
08712 
08713    return 0;
08714 }

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

08568 {
08569    struct ao2_iterator im;
08570    struct member *member;
08571    struct queue_ent *qe;
08572    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08573    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08574 
08575    data_queue = ast_data_add_node(data_root, "queue");
08576    if (!data_queue) {
08577       return;
08578    }
08579 
08580    ast_data_add_structure(call_queue, data_queue, queue);
08581 
08582    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08583    ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08584 
08585    /* announce position */
08586    enum_node = ast_data_add_node(data_queue, "announceposition");
08587    if (!enum_node) {
08588       return;
08589    }
08590    switch (queue->announceposition) {
08591    case ANNOUNCEPOSITION_LIMIT:
08592       ast_data_add_str(enum_node, "text", "limit");
08593       break;
08594    case ANNOUNCEPOSITION_MORE_THAN:
08595       ast_data_add_str(enum_node, "text", "more");
08596       break;
08597    case ANNOUNCEPOSITION_YES:
08598       ast_data_add_str(enum_node, "text", "yes");
08599       break;
08600    case ANNOUNCEPOSITION_NO:
08601       ast_data_add_str(enum_node, "text", "no");
08602       break;
08603    default:
08604       ast_data_add_str(enum_node, "text", "unknown");
08605       break;
08606    }
08607    ast_data_add_int(enum_node, "value", queue->announceposition);
08608 
08609    /* add queue members */
08610    im = ao2_iterator_init(queue->members, 0);
08611    while ((member = ao2_iterator_next(&im))) {
08612       if (!data_members) {
08613          data_members = ast_data_add_node(data_queue, "members");
08614          if (!data_members) {
08615             ao2_ref(member, -1);
08616             continue;
08617          }
08618       }
08619 
08620       data_member = ast_data_add_node(data_members, "member");
08621       if (!data_member) {
08622          ao2_ref(member, -1);
08623          continue;
08624       }
08625 
08626       ast_data_add_structure(member, data_member, member);
08627 
08628       ao2_ref(member, -1);
08629    }
08630    ao2_iterator_destroy(&im);
08631 
08632    /* include the callers inside the result. */
08633    if (queue->head) {
08634       for (qe = queue->head; qe; qe = qe->next) {
08635          if (!data_callers) {
08636             data_callers = ast_data_add_node(data_queue, "callers");
08637             if (!data_callers) {
08638                continue;
08639             }
08640          }
08641 
08642          data_caller = ast_data_add_node(data_callers, "caller");
08643          if (!data_caller) {
08644             continue;
08645          }
08646 
08647          ast_data_add_structure(queue_ent, data_caller, qe);
08648 
08649          /* add the caller channel. */
08650          data_caller_channel = ast_data_add_node(data_caller, "channel");
08651          if (!data_caller_channel) {
08652             continue;
08653          }
08654 
08655          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08656       }
08657    }
08658 
08659    /* if this queue doesn't match remove the added queue. */
08660    if (!ast_data_search_match(search, data_queue)) {
08661       ast_data_remove_node(data_root, data_queue);
08662    }
08663 }

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

Definition at line 2840 of file app_queue.c.

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

Referenced by try_calling().

02841 {
02842    int oldvalue;
02843 
02844    /* Calculate holdtime using an exponential average */
02845    /* Thanks to SRT for this contribution */
02846    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02847 
02848    ao2_lock(qe->parent);
02849    oldvalue = qe->parent->holdtime;
02850    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02851    ao2_unlock(qe->parent);
02852 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

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

03532 {
03533    set_queue_variables(qe->parent, qe->chan);
03534    ao2_lock(qe->parent);
03535    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03536       "Queue: %s\r\n"
03537       "Uniqueid: %s\r\n"
03538       "Position: %d\r\n"
03539       "OriginalPosition: %d\r\n"
03540       "HoldTime: %d\r\n",
03541       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03542 
03543    qe->parent->callsabandoned++;
03544    ao2_unlock(qe->parent);
03545 }

static int reload ( void   )  [static]

Definition at line 8848 of file app_queue.c.

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

08849 {
08850    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08851    ast_unload_realtime("queue_members");
08852    reload_handler(1, &mask, NULL);
08853    return 0;
08854 }

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

07224 {
07225    int res = 0;
07226 
07227    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
07228       res |= reload_queue_rules(reload);
07229    }
07230    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
07231       res |= clear_stats(queuename);
07232    }
07233    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
07234       res |= reload_queues(reload, mask, queuename);
07235    }
07236    return res;
07237 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

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

05765 {
05766    char *cur_ptr;
05767    const char *queue_name;
05768    char *member;
05769    char *interface;
05770    char *membername = NULL;
05771    char *state_interface;
05772    char *penalty_tok;
05773    int penalty = 0;
05774    char *paused_tok;
05775    int paused = 0;
05776    struct ast_db_entry *db_tree;
05777    struct ast_db_entry *entry;
05778    struct call_queue *cur_queue;
05779    char *queue_data;
05780 
05781    /* Each key in 'pm_family' is the name of a queue */
05782    db_tree = ast_db_gettree(pm_family, NULL);
05783    for (entry = db_tree; entry; entry = entry->next) {
05784 
05785       queue_name = entry->key + strlen(pm_family) + 2;
05786 
05787       {
05788          struct call_queue tmpq = {
05789             .name = queue_name,
05790          };
05791          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05792       }  
05793 
05794       if (!cur_queue)
05795          cur_queue = load_realtime_queue(queue_name);
05796 
05797       if (!cur_queue) {
05798          /* If the queue no longer exists, remove it from the
05799           * database */
05800          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05801          ast_db_del(pm_family, queue_name);
05802          continue;
05803       } 
05804 
05805       if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
05806          queue_t_unref(cur_queue, "Expire reload reference");
05807          continue;
05808       }
05809 
05810       cur_ptr = queue_data;
05811       while ((member = strsep(&cur_ptr, ",|"))) {
05812          if (ast_strlen_zero(member))
05813             continue;
05814 
05815          interface = strsep(&member, ";");
05816          penalty_tok = strsep(&member, ";");
05817          paused_tok = strsep(&member, ";");
05818          membername = strsep(&member, ";");
05819          state_interface = strsep(&member, ";");
05820 
05821          if (!penalty_tok) {
05822             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05823             break;
05824          }
05825          penalty = strtol(penalty_tok, NULL, 10);
05826          if (errno == ERANGE) {
05827             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05828             break;
05829          }
05830          
05831          if (!paused_tok) {
05832             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05833             break;
05834          }
05835          paused = strtol(paused_tok, NULL, 10);
05836          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05837             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05838             break;
05839          }
05840 
05841          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05842          
05843          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05844             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05845             break;
05846          }
05847       }
05848       queue_t_unref(cur_queue, "Expire reload reference");
05849       ast_free(queue_data);
05850    }
05851 
05852    if (db_tree) {
05853       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05854       ast_db_freetree(db_tree);
05855    }
05856 }

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

06799 {
06800    struct ast_config *cfg;
06801    struct rule_list *rl_iter, *new_rl;
06802    struct penalty_rule *pr_iter;
06803    char *rulecat = NULL;
06804    struct ast_variable *rulevar = NULL;
06805    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06806    
06807    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06808       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06809       return AST_MODULE_LOAD_SUCCESS;
06810    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06811       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06812       return AST_MODULE_LOAD_SUCCESS;
06813    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06814       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
06815       return AST_MODULE_LOAD_SUCCESS;
06816    }
06817 
06818    AST_LIST_LOCK(&rule_lists);
06819    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06820       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06821          ast_free(pr_iter);
06822       ast_free(rl_iter);
06823    }
06824    while ((rulecat = ast_category_browse(cfg, rulecat))) {
06825       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06826          AST_LIST_UNLOCK(&rule_lists);
06827          ast_config_destroy(cfg);
06828          return AST_MODULE_LOAD_FAILURE;
06829       } else {
06830          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06831          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06832          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06833             if(!strcasecmp(rulevar->name, "penaltychange"))
06834                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06835             else
06836                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06837       }
06838    }
06839    AST_LIST_UNLOCK(&rule_lists);
06840 
06841    ast_config_destroy(cfg);
06842 
06843    return AST_MODULE_LOAD_SUCCESS;
06844 }

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

07129 {
07130    struct ast_config *cfg;
07131    char *cat;
07132    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07133    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
07134    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
07135 
07136    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
07137       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
07138       return -1;
07139    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07140       return 0;
07141    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07142       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
07143       return -1;
07144    }
07145 
07146    /* We've made it here, so it looks like we're doing operations on all queues. */
07147    ao2_lock(queues);
07148 
07149    /* Mark all queues as dead for the moment if we're reloading queues.
07150     * For clarity, we could just be reloading members, in which case we don't want to mess
07151     * with the other queue parameters at all*/
07152    if (queue_reload) {
07153       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
07154    }
07155 
07156    if (member_reload) {
07157       ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename);
07158    }
07159 
07160    /* Chug through config file */
07161    cat = NULL;
07162    while ((cat = ast_category_browse(cfg, cat)) ) {
07163       if (!strcasecmp(cat, "general") && queue_reload) {
07164          queue_set_global_params(cfg);
07165          continue;
07166       }
07167       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
07168          reload_single_queue(cfg, mask, cat);
07169    }
07170 
07171    ast_config_destroy(cfg);
07172    /* Unref all the dead queues if we were reloading queues */
07173    if (queue_reload) {
07174       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
07175    }
07176    ao2_unlock(queues);
07177    return 0;
07178 }

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

06878 {
06879    char *membername, *interface, *state_interface, *tmp;
06880    char *parse;
06881    struct member *cur, *newm;
06882    struct member tmpmem;
06883    int penalty;
06884    AST_DECLARE_APP_ARGS(args,
06885       AST_APP_ARG(interface);
06886       AST_APP_ARG(penalty);
06887       AST_APP_ARG(membername);
06888       AST_APP_ARG(state_interface);
06889    );
06890 
06891    if (ast_strlen_zero(memberdata)) {
06892       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06893       return;
06894    }
06895 
06896    /* Add a new member */
06897    parse = ast_strdupa(memberdata);
06898             
06899    AST_STANDARD_APP_ARGS(args, parse);
06900 
06901    interface = args.interface;
06902    if (!ast_strlen_zero(args.penalty)) {
06903       tmp = args.penalty;
06904       ast_strip(tmp);
06905       penalty = atoi(tmp);
06906       if (penalty < 0) {
06907          penalty = 0;
06908       }
06909    } else {
06910       penalty = 0;
06911    }
06912 
06913    if (!ast_strlen_zero(args.membername)) {
06914       membername = args.membername;
06915       ast_strip(membername);
06916    } else {
06917       membername = interface;
06918    }
06919 
06920    if (!ast_strlen_zero(args.state_interface)) {
06921       state_interface = args.state_interface;
06922       ast_strip(state_interface);
06923    } else {
06924       state_interface = interface;
06925    }
06926 
06927    /* Find the old position in the list */
06928    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06929    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
06930 
06931    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06932       if (cur) {
06933          /* Round Robin Queue Position must be copied if this is replacing an existing member */
06934          ao2_lock(q->members);
06935          newm->queuepos = cur->queuepos;
06936          ao2_link(q->members, newm);
06937          ao2_unlink(q->members, cur);
06938          ao2_unlock(q->members);
06939       } else {
06940          /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
06941          member_add_to_queue(q, newm);
06942       }
06943       ao2_ref(newm, -1);
06944    }
06945    newm = NULL;
06946 
06947    if (cur) {
06948       ao2_ref(cur, -1);
06949    }
06950 }

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

06985 {
06986    int new;
06987    struct call_queue *q = NULL;
06988    /*We're defining a queue*/
06989    struct call_queue tmpq = {
06990       .name = queuename,
06991    };
06992    const char *tmpvar;
06993    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06994    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
06995    int prev_weight = 0;
06996    struct ast_variable *var;
06997    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
06998       if (queue_reload) {
06999          /* Make one then */
07000          if (!(q = alloc_queue(queuename))) {
07001             return;
07002          }
07003       } else {
07004          /* Since we're not reloading queues, this means that we found a queue
07005           * in the configuration file which we don't know about yet. Just return.
07006           */
07007          return;
07008       }
07009       new = 1;
07010    } else {
07011       new = 0;
07012    }
07013    
07014    if (!new) {
07015       ao2_lock(q);
07016       prev_weight = q->weight ? 1 : 0;
07017    }
07018    /* Check if we already found a queue with this name in the config file */
07019    if (q->found) {
07020       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
07021       if (!new) {
07022          /* It should be impossible to *not* hit this case*/
07023          ao2_unlock(q);
07024       }
07025       queue_t_unref(q, "We exist! Expiring temporary pointer");
07026       return;
07027    }
07028    /* Due to the fact that the "linear" strategy will have a different allocation
07029     * scheme for queue members, we must devise the queue's strategy before other initializations.
07030     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
07031     * container used will have only a single bucket instead of the typical number.
07032     */
07033    if (queue_reload) {
07034       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
07035          q->strategy = strat2int(tmpvar);
07036          if (q->strategy < 0) {
07037             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
07038             tmpvar, q->name);
07039             q->strategy = QUEUE_STRATEGY_RINGALL;
07040          }
07041       } else {
07042          q->strategy = QUEUE_STRATEGY_RINGALL;
07043       }
07044       init_queue(q);
07045    }
07046    if (member_reload) {
07047       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
07048       q->found = 1;
07049    }
07050    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
07051       if (member_reload && !strcasecmp(var->name, "member")) {
07052          reload_single_member(var->value, q);
07053       } else if (queue_reload) {
07054          queue_set_param(q, var->name, var->value, var->lineno, 1);
07055       }
07056    }
07057    /* At this point, we've determined if the queue has a weight, so update use_weight
07058     * as appropriate
07059     */
07060    if (!q->weight && prev_weight) {
07061       ast_atomic_fetchadd_int(&use_weight, -1);
07062    }
07063    else if (q->weight && !prev_weight) {
07064       ast_atomic_fetchadd_int(&use_weight, +1);
07065    }
07066 
07067    /* Free remaining members marked as delme */
07068    if (member_reload) {
07069       ao2_lock(q->members);
07070       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q);
07071       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
07072       ao2_unlock(q->members);
07073    }
07074 
07075    if (new) {
07076       queues_t_link(queues, q, "Add queue to container");
07077    } else {
07078       ao2_unlock(q);
07079    }
07080    queue_t_unref(q, "Expiring creation reference");
07081 }

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

05508 {
05509    struct call_queue *q, tmpq = {
05510       .name = queuename,   
05511    };
05512    struct member *mem, tmpmem;
05513    int res = RES_NOSUCHQUEUE;
05514 
05515    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05516    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05517       ao2_lock(q);
05518       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05519          /* XXX future changes should beware of this assumption!! */
05520          if (!mem->dynamic) {
05521             ao2_ref(mem, -1);
05522             ao2_unlock(q);
05523             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05524             return RES_NOT_DYNAMIC;
05525          }
05526          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05527             "Queue: %s\r\n"
05528             "Location: %s\r\n"
05529             "MemberName: %s\r\n",
05530             q->name, mem->interface, mem->membername);
05531          member_remove_from_queue(q, mem);
05532          ao2_ref(mem, -1);
05533 
05534          if (queue_persistent_members)
05535             dump_queue_members(q);
05536          
05537          res = RES_OKAY;
05538       } else {
05539          res = RES_EXISTS;
05540       }
05541       ao2_unlock(q);
05542       queue_t_unref(q, "Expiring temporary reference");
05543    }
05544 
05545    return res;
05546 }

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

Definition at line 7083 of file app_queue.c.

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

Referenced by reload_queues().

07084 {
07085    struct call_queue *q = obj;
07086    char *queuename = arg;
07087    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07088       q->found = 0;
07089 
07090    }
07091    return 0;
07092 }

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 3207 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_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, 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, queue_ent::parent, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, ast_channel::redirecting, call_queue::ringinuse, call_queue::rrpos, S_COR, S_OR, ast_cdr::src, 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().

03208 {
03209    int res;
03210    int status;
03211    char tech[256];
03212    char *location;
03213    const char *macrocontext, *macroexten;
03214 
03215    /* on entry here, we know that tmp->chan == NULL */
03216    if (!can_ring_entry(qe, tmp)) {
03217       if (qe->chan->cdr) {
03218          ast_cdr_busy(qe->chan->cdr);
03219       }
03220       tmp->stillgoing = 0;
03221       ++*busies;
03222       return 0;
03223    }
03224    ast_assert(qe->parent->ringinuse || tmp->member->call_pending);
03225 
03226    ast_copy_string(tech, tmp->interface, sizeof(tech));
03227    if ((location = strchr(tech, '/')))
03228       *location++ = '\0';
03229    else
03230       location = "";
03231 
03232    /* Request the peer */
03233    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03234    if (!tmp->chan) {       /* If we can't, just go on to the next call */
03235       ao2_lock(qe->parent);
03236       qe->parent->rrpos++;
03237       qe->linpos++;
03238       ao2_unlock(qe->parent);
03239 
03240       member_call_pending_clear(tmp->member);
03241 
03242       if (qe->chan->cdr) {
03243          ast_cdr_busy(qe->chan->cdr);
03244       }
03245       tmp->stillgoing = 0;
03246       ++*busies;
03247       return 0;
03248    }
03249 
03250    ast_channel_lock_both(tmp->chan, qe->chan);
03251 
03252    if (qe->cancel_answered_elsewhere) {
03253       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03254    }
03255    tmp->chan->appl = "AppQueue";
03256    tmp->chan->data = "(Outgoing Line)";
03257    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03258 
03259    /* If the new channel has no callerid, try to guess what it should be */
03260    if (!tmp->chan->caller.id.number.valid) {
03261       if (qe->chan->connected.id.number.valid) {
03262          struct ast_party_caller caller;
03263 
03264          ast_party_caller_set_init(&caller, &tmp->chan->caller);
03265          caller.id = qe->chan->connected.id;
03266          caller.ani = qe->chan->connected.ani;
03267          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03268       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03269          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03270       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03271          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
03272       }
03273       tmp->dial_callerid_absent = 1;
03274    }
03275 
03276    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03277 
03278    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03279 
03280    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03281 
03282    /* Inherit specially named variables from parent channel */
03283    ast_channel_inherit_variables(qe->chan, tmp->chan);
03284    ast_channel_datastore_inherit(qe->chan, tmp->chan);
03285 
03286    /* Presense of ADSI CPE on outgoing channel follows ours */
03287    tmp->chan->adsicpe = qe->chan->adsicpe;
03288 
03289    /* Inherit context and extension */
03290    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03291    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03292    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03293    if (!ast_strlen_zero(macroexten))
03294       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03295    else
03296       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03297    if (ast_cdr_isset_unanswered()) {
03298       /* they want to see the unanswered dial attempts! */
03299       /* set up the CDR fields on all the CDRs to give sensical information */
03300       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
03301       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03302       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03303       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03304       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03305       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03306       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03307       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03308       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03309       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03310       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03311    }
03312 
03313    ast_channel_unlock(tmp->chan);
03314    ast_channel_unlock(qe->chan);
03315 
03316    /* Place the call, but don't wait on the answer */
03317    if ((res = ast_call(tmp->chan, location, 0))) {
03318       /* Again, keep going even if there's an error */
03319       ast_verb(3, "Couldn't call %s\n", tmp->interface);
03320       do_hang(tmp);
03321       member_call_pending_clear(tmp->member);
03322       ++*busies;
03323       return 0;
03324    }
03325 
03326    if (qe->parent->eventwhencalled) {
03327       char vars[2048];
03328 
03329       ast_channel_lock_both(tmp->chan, qe->chan);
03330 
03331       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03332          "Queue: %s\r\n"
03333          "AgentCalled: %s\r\n"
03334          "AgentName: %s\r\n"
03335          "ChannelCalling: %s\r\n"
03336          "DestinationChannel: %s\r\n"
03337          "CallerIDNum: %s\r\n"
03338          "CallerIDName: %s\r\n"
03339          "ConnectedLineNum: %s\r\n"
03340          "ConnectedLineName: %s\r\n"
03341          "Context: %s\r\n"
03342          "Extension: %s\r\n"
03343          "Priority: %d\r\n"
03344          "Uniqueid: %s\r\n"
03345          "%s",
03346          qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03347          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
03348          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
03349          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
03350          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
03351          qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03352          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03353 
03354       ast_channel_unlock(tmp->chan);
03355       ast_channel_unlock(qe->chan);
03356 
03357       ast_verb(3, "Called %s\n", tmp->interface);
03358    }
03359 
03360    member_call_pending_clear(tmp->member);
03361    return 1;
03362 }

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

03391 {
03392    int ret = 0;
03393 
03394    while (ret == 0) {
03395       struct callattempt *best = find_best(outgoing);
03396       if (!best) {
03397          ast_debug(1, "Nobody left to try ringing in queue\n");
03398          break;
03399       }
03400       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03401          struct callattempt *cur;
03402          /* Ring everyone who shares this best metric (for ringall) */
03403          for (cur = outgoing; cur; cur = cur->q_next) {
03404             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03405                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03406                ret |= ring_entry(qe, cur, busies);
03407             }
03408          }
03409       } else {
03410          /* Ring just the best channel */
03411          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03412          ret = ring_entry(qe, best, busies);
03413       }
03414       
03415       /* If we have timed out, break out */
03416       if (qe->expire && (time(NULL) >= qe->expire)) {
03417          ast_debug(1, "Queue timed out while ringing members.\n");
03418          ret = 0;
03419          break;
03420       }
03421    }
03422 
03423    return ret;
03424 }

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

03549 {
03550    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03551 
03552    /* Stop ringing, and resume MOH if specified */
03553    if (qe->ring_when_ringing) {
03554       ast_indicate(qe->chan, -1);
03555       ast_moh_start(qe->chan, qe->moh, NULL);
03556    }
03557 
03558    if (qe->parent->eventwhencalled) {
03559       char vars[2048];
03560 
03561       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03562                   "Queue: %s\r\n"
03563                   "Uniqueid: %s\r\n"
03564                   "Channel: %s\r\n"
03565                   "Member: %s\r\n"
03566                   "MemberName: %s\r\n"
03567                   "Ringtime: %d\r\n"
03568                   "%s",
03569                   qe->parent->name,
03570                   qe->chan->uniqueid,
03571                   qe->chan->name,
03572                   interface,
03573                   membername,
03574                   rnatime,
03575                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03576    }
03577    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03578    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03579       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03580          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03581             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03582                interface, qe->parent->name);
03583          } else {
03584             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03585          }
03586       } else {
03587          /* If queue autopause is mode all, just don't send any queue to stop.
03588          * the function will stop in all queues */
03589          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03590             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03591                   interface, qe->parent->name);
03592          } else {
03593                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03594          }
03595       }
03596    }
03597    return;
03598 }

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

RemoveQueueMember application.

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

05932 {
05933    int res=-1;
05934    char *parse, *temppos = NULL;
05935    AST_DECLARE_APP_ARGS(args,
05936       AST_APP_ARG(queuename);
05937       AST_APP_ARG(interface);
05938    );
05939 
05940 
05941    if (ast_strlen_zero(data)) {
05942       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
05943       return -1;
05944    }
05945 
05946    parse = ast_strdupa(data);
05947 
05948    AST_STANDARD_APP_ARGS(args, parse);
05949 
05950    if (ast_strlen_zero(args.interface)) {
05951       args.interface = ast_strdupa(chan->name);
05952       temppos = strrchr(args.interface, '-');
05953       if (temppos)
05954          *temppos = '\0';
05955    }
05956 
05957    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05958 
05959    switch (remove_from_queue(args.queuename, args.interface)) {
05960    case RES_OKAY:
05961       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05962       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05963       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05964       res = 0;
05965       break;
05966    case RES_EXISTS:
05967       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05968       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05969       res = 0;
05970       break;
05971    case RES_NOSUCHQUEUE:
05972       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05973       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05974       res = 0;
05975       break;
05976    case RES_NOT_DYNAMIC:
05977       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05978       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05979       res = 0;
05980       break;
05981    }
05982 
05983    return res;
05984 }

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

02176 {
02177    struct member *m;
02178    struct ao2_iterator mem_iter;
02179    int penalty = 0;
02180    int paused  = 0;
02181    int found = 0;
02182 
02183    if (ast_strlen_zero(rt_uniqueid)) {
02184       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02185       return;
02186    }
02187 
02188    if (penalty_str) {
02189       penalty = atoi(penalty_str);
02190       if (penalty < 0)
02191          penalty = 0;
02192    }
02193 
02194    if (paused_str) {
02195       paused = atoi(paused_str);
02196       if (paused < 0)
02197          paused = 0;
02198    }
02199 
02200    /* Find member by realtime uniqueid and update */
02201    mem_iter = ao2_iterator_init(q->members, 0);
02202    while ((m = ao2_iterator_next(&mem_iter))) {
02203       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02204          m->dead = 0;   /* Do not delete this one. */
02205          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02206          if (paused_str)
02207             m->paused = paused;
02208          if (strcasecmp(state_interface, m->state_interface)) {
02209             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02210          }     
02211          m->penalty = penalty;
02212          found = 1;
02213          ao2_ref(m, -1);
02214          break;
02215       }
02216       ao2_ref(m, -1);
02217    }
02218    ao2_iterator_destroy(&mem_iter);
02219 
02220    /* Create a new member */
02221    if (!found) {
02222       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02223          m->dead = 0;
02224          m->realtime = 1;
02225          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02226          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02227          member_add_to_queue(q, m);
02228          ao2_ref(m, -1);
02229          m = NULL;
02230       }
02231    }
02232 }

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

Playback announcement to queued members if period has elapsed.

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

03476 {
03477    int res = 0;
03478    time_t now;
03479 
03480    /* Get the current time */
03481    time(&now);
03482 
03483    /* Check to see if it is time to announce */
03484    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03485       return 0;
03486 
03487    /* Stop the music on hold so we can play our own file */
03488    if (ringing)
03489       ast_indicate(qe->chan,-1);
03490    else
03491       ast_moh_stop(qe->chan);
03492 
03493    ast_verb(3, "Playing periodic announcement\n");
03494    
03495    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03496       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03497    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03498       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03499       qe->last_periodic_announce_sound = 0;
03500    }
03501    
03502    /* play the announcement */
03503    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03504 
03505    if (res > 0 && !valid_exit(qe, res))
03506       res = 0;
03507 
03508    /* Resume Music on Hold if the caller is going to stay in the queue */
03509    if (!res) {
03510       if (ringing)
03511          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03512       else
03513          ast_moh_start(qe->chan, qe->moh, NULL);
03514    }
03515 
03516    /* update last_periodic_announce_time */
03517    if (qe->parent->relativeperiodicannounce)
03518       time(&qe->last_periodic_announce_time);
03519    else
03520       qe->last_periodic_announce_time = now;
03521 
03522    /* Update the current periodic announcement to the next announcement */
03523    if (!qe->parent->randomperiodicannounce) {
03524       qe->last_periodic_announce_sound++;
03525    }
03526    
03527    return res;
03528 }

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

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

02697 {
02698    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02699    int say_thanks = 1;
02700    time_t now;
02701 
02702    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02703    time(&now);
02704    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02705       return 0;
02706 
02707    /* If either our position has changed, or we are over the freq timer, say position */
02708    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02709       return 0;
02710 
02711    if (ringing) {
02712       ast_indicate(qe->chan,-1);
02713    } else {
02714       ast_moh_stop(qe->chan);
02715    }
02716 
02717    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02718       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02719       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02720       qe->pos <= qe->parent->announcepositionlimit))
02721          announceposition = 1;
02722 
02723 
02724    if (announceposition == 1) {
02725       /* Say we're next, if we are */
02726       if (qe->pos == 1) {
02727          res = play_file(qe->chan, qe->parent->sound_next);
02728          if (res)
02729             goto playout;
02730          else
02731             goto posout;
02732       } else {
02733          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02734             /* More than Case*/
02735             res = play_file(qe->chan, qe->parent->queue_quantity1);
02736             if (res)
02737                goto playout;
02738             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02739             if (res)
02740                goto playout;
02741          } else {
02742             /* Normal Case */
02743             res = play_file(qe->chan, qe->parent->sound_thereare);
02744             if (res)
02745                goto playout;
02746             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02747             if (res)
02748                goto playout;
02749          }
02750          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02751             /* More than Case*/
02752             res = play_file(qe->chan, qe->parent->queue_quantity2);
02753             if (res)
02754                goto playout;
02755          } else {
02756             res = play_file(qe->chan, qe->parent->sound_calls);
02757             if (res)
02758                goto playout;
02759          }
02760       }
02761    }
02762    /* Round hold time to nearest minute */
02763    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02764 
02765    /* If they have specified a rounding then round the seconds as well */
02766    if (qe->parent->roundingseconds) {
02767       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02768       avgholdsecs *= qe->parent->roundingseconds;
02769    } else {
02770       avgholdsecs = 0;
02771    }
02772 
02773    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02774 
02775    /* If the hold time is >1 min, if it's enabled, and if it's not
02776       supposed to be only once and we have already said it, say it */
02777     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02778         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02779         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02780       res = play_file(qe->chan, qe->parent->sound_holdtime);
02781       if (res)
02782          goto playout;
02783 
02784       if (avgholdmins >= 1) {
02785          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02786          if (res)
02787             goto playout;
02788 
02789          if (avgholdmins == 1) {
02790             res = play_file(qe->chan, qe->parent->sound_minute);
02791             if (res)
02792                goto playout;
02793          } else {
02794             res = play_file(qe->chan, qe->parent->sound_minutes);
02795             if (res)
02796                goto playout;
02797          }
02798       }
02799       if (avgholdsecs >= 1) {
02800          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02801          if (res)
02802             goto playout;
02803 
02804          res = play_file(qe->chan, qe->parent->sound_seconds);
02805          if (res)
02806             goto playout;
02807       }
02808    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02809       say_thanks = 0;
02810    }
02811 
02812 posout:
02813    if (qe->parent->announceposition) {
02814       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02815          qe->chan->name, qe->parent->name, qe->pos);
02816    }
02817    if (say_thanks) {
02818       res = play_file(qe->chan, qe->parent->sound_thanks);
02819    }
02820 playout:
02821 
02822    if ((res > 0 && !valid_exit(qe, res)))
02823       res = 0;
02824 
02825    /* Set our last_pos indicators */
02826    qe->last_pos = now;
02827    qe->last_pos_said = qe->pos;
02828 
02829    /* Don't restart music on hold if we're about to exit the caller from the queue */
02830    if (!res) {
02831       if (ringing) {
02832          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02833       } else {
02834          ast_moh_start(qe->chan, qe->moh, NULL);
02835       }
02836    }
02837    return res;
02838 }

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

04441 {
04442    const char *reason = NULL; /* silence dumb compilers */
04443 
04444    if (!qe->parent->eventwhencalled)
04445       return;
04446 
04447    switch (rsn) {
04448    case CALLER:
04449       reason = "caller";
04450       break;
04451    case AGENT:
04452       reason = "agent";
04453       break;
04454    case TRANSFER:
04455       reason = "transfer";
04456       break;
04457    }
04458 
04459    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04460       "Queue: %s\r\n"
04461       "Uniqueid: %s\r\n"
04462       "Channel: %s\r\n"
04463       "Member: %s\r\n"
04464       "MemberName: %s\r\n"
04465       "HoldTime: %ld\r\n"
04466       "TalkTime: %ld\r\n"
04467       "Reason: %s\r\n"
04468       "%s",
04469       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04470       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04471       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04472 }

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

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

05607 {
05608    int found = 0;
05609    struct call_queue *q;
05610    struct member *mem;
05611    struct ao2_iterator queue_iter;
05612    int failed;
05613 
05614    /* Special event for when all queues are paused - individual events still generated */
05615    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05616    if (ast_strlen_zero(queuename))
05617       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05618 
05619    queue_iter = ao2_iterator_init(queues, 0);
05620    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05621       ao2_lock(q);
05622       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05623          if ((mem = interface_exists(q, interface))) {
05624             if (mem->paused == paused) {
05625                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05626             }
05627 
05628             failed = 0;
05629             if (mem->realtime) {
05630                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05631             }
05632          
05633             if (failed) {
05634                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05635                ao2_ref(mem, -1);
05636                ao2_unlock(q);
05637                queue_t_unref(q, "Done with iterator");
05638                continue;
05639             }  
05640             found++;
05641             mem->paused = paused;
05642 
05643             if (queue_persistent_members)
05644                dump_queue_members(q);
05645 
05646             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05647             
05648             if (!ast_strlen_zero(reason)) {
05649                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05650                   "Queue: %s\r\n"
05651                   "Location: %s\r\n"
05652                   "MemberName: %s\r\n"
05653                   "Paused: %d\r\n"
05654                   "Reason: %s\r\n",
05655                      q->name, mem->interface, mem->membername, paused, reason);
05656             } else {
05657                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05658                   "Queue: %s\r\n"
05659                   "Location: %s\r\n"
05660                   "MemberName: %s\r\n"
05661                   "Paused: %d\r\n",
05662                      q->name, mem->interface, mem->membername, paused);
05663             }
05664             ao2_ref(mem, -1);
05665          }
05666       }
05667       
05668       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05669          ao2_unlock(q);
05670          queue_t_unref(q, "Done with iterator");
05671          break;
05672       }
05673       
05674       ao2_unlock(q);
05675       queue_t_unref(q, "Done with iterator");
05676    }
05677    ao2_iterator_destroy(&queue_iter);
05678 
05679    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05680 }

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

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

05684 {
05685    int foundinterface = 0, foundqueue = 0;
05686    struct call_queue *q;
05687    struct member *mem;
05688    struct ao2_iterator queue_iter;
05689 
05690    if (penalty < 0) {
05691       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05692       return RESULT_FAILURE;
05693    }
05694 
05695    queue_iter = ao2_iterator_init(queues, 0);
05696    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05697       ao2_lock(q);
05698       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05699          foundqueue++;
05700          if ((mem = interface_exists(q, interface))) {
05701             foundinterface++;
05702             mem->penalty = penalty;
05703             
05704             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05705             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05706                "Queue: %s\r\n"
05707                "Location: %s\r\n"
05708                "Penalty: %d\r\n",
05709                q->name, mem->interface, penalty);
05710             ao2_ref(mem, -1);
05711          }
05712       }
05713       ao2_unlock(q);
05714       queue_t_unref(q, "Done with iterator");
05715    }
05716    ao2_iterator_destroy(&queue_iter);
05717 
05718    if (foundinterface) {
05719       return RESULT_SUCCESS;
05720    } else if (!foundqueue) {
05721       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05722    } else {
05723       ast_log (LOG_ERROR, "Invalid interface\n");
05724    }  
05725 
05726    return RESULT_FAILURE;
05727 }

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

sets the QUEUESTATUS channel variable

Definition at line 1197 of file app_queue.c.

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

Referenced by queue_exec().

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

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

Set variables of queue.

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

01347 {
01348    char interfacevar[256]="";
01349    float sl = 0;
01350 
01351    ao2_lock(q);
01352 
01353    if (q->setqueuevar) {
01354       sl = 0;
01355       if (q->callscompleted > 0) 
01356          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01357 
01358       snprintf(interfacevar, sizeof(interfacevar),
01359          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01360          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01361 
01362       ao2_unlock(q);
01363    
01364       pbx_builtin_setvar_multiple(chan, interfacevar); 
01365    } else {
01366       ao2_unlock(q);
01367    }
01368 }

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

04543 {
04544    struct ast_datastore *ds;
04545    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04546 
04547    if (!qtds) {
04548       ast_log(LOG_WARNING, "Memory allocation error!\n");
04549       return NULL;
04550    }
04551 
04552    ast_channel_lock(qe->chan);
04553    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04554       ast_channel_unlock(qe->chan);
04555       ast_free(qtds);
04556       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04557       return NULL;
04558    }
04559 
04560    qtds->qe = qe;
04561    /* This member is refcounted in try_calling, so no need to add it here, too */
04562    qtds->member = member;
04563    qtds->starttime = starttime;
04564    qtds->callcompletedinsl = callcompletedinsl;
04565    ds->data = qtds;
04566    ast_channel_datastore_add(qe->chan, ds);
04567    ast_channel_unlock(qe->chan);
04568    return ds;
04569 }

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

03452 {
03453    struct callattempt *best = find_best(outgoing);
03454 
03455    if (best) {
03456       /* Ring just the best channel */
03457       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03458       qe->linpos = best->metric % 1000;
03459    } else {
03460       /* Just increment rrpos */
03461       if (qe->linwrapped) {
03462          /* No more channels, start over */
03463          qe->linpos = 0;
03464       } else {
03465          /* Prioritize next entry */
03466          qe->linpos++;
03467       }
03468    }
03469    qe->linwrapped = 0;
03470 
03471    return 0;
03472 }

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

03428 {
03429    struct callattempt *best = find_best(outgoing);
03430 
03431    if (best) {
03432       /* Ring just the best channel */
03433       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03434       qe->parent->rrpos = best->metric % 1000;
03435    } else {
03436       /* Just increment rrpos */
03437       if (qe->parent->wrapped) {
03438          /* No more channels, start over */
03439          qe->parent->rrpos = 0;
03440       } else {
03441          /* Prioritize next entry */
03442          qe->parent->rrpos++;
03443       }
03444    }
03445    qe->parent->wrapped = 0;
03446 
03447    return 0;
03448 }

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

Definition at line 1221 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

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

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 4623 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_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, 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(), 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, 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().

04624 {
04625    struct member *cur;
04626    struct callattempt *outgoing = NULL; /* the list of calls we are building */
04627    int to, orig;
04628    char oldexten[AST_MAX_EXTENSION]="";
04629    char oldcontext[AST_MAX_CONTEXT]="";
04630    char queuename[256]="";
04631    char interfacevar[256]="";
04632    struct ast_channel *peer;
04633    struct ast_channel *which;
04634    struct callattempt *lpeer;
04635    struct member *member;
04636    struct ast_app *application;
04637    int res = 0, bridge = 0;
04638    int numbusies = 0;
04639    int x=0;
04640    char *announce = NULL;
04641    char digit = 0;
04642    time_t callstart;
04643    time_t now = time(NULL);
04644    struct ast_bridge_config bridge_config;
04645    char nondataquality = 1;
04646    char *agiexec = NULL;
04647    char *macroexec = NULL;
04648    char *gosubexec = NULL;
04649    const char *monitorfilename;
04650    const char *monitor_exec;
04651    const char *monitor_options;
04652    char tmpid[256], tmpid2[256];
04653    char meid[1024], meid2[1024];
04654    char mixmonargs[1512];
04655    struct ast_app *mixmonapp = NULL;
04656    char *p;
04657    char vars[2048];
04658    int forwardsallowed = 1;
04659    int block_connected_line = 0;
04660    int callcompletedinsl;
04661    struct ao2_iterator memi;
04662    struct ast_datastore *datastore, *transfer_ds;
04663    struct queue_end_bridge *queue_end_bridge = NULL;
04664 
04665    ast_channel_lock(qe->chan);
04666    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04667    ast_channel_unlock(qe->chan);
04668 
04669    memset(&bridge_config, 0, sizeof(bridge_config));
04670    tmpid[0] = 0;
04671    meid[0] = 0;
04672    time(&now);
04673 
04674    /* If we've already exceeded our timeout, then just stop
04675     * This should be extremely rare. queue_exec will take care
04676     * of removing the caller and reporting the timeout as the reason.
04677     */
04678    if (qe->expire && now >= qe->expire) {
04679       res = 0;
04680       goto out;
04681    }
04682       
04683    for (; options && *options; options++)
04684       switch (*options) {
04685       case 't':
04686          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04687          break;
04688       case 'T':
04689          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04690          break;
04691       case 'w':
04692          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04693          break;
04694       case 'W':
04695          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04696          break;
04697       case 'c':
04698          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04699          break;
04700       case 'd':
04701          nondataquality = 0;
04702          break;
04703       case 'h':
04704          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04705          break;
04706       case 'H':
04707          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04708          break;
04709       case 'k':
04710          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04711          break;
04712       case 'K':
04713          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04714          break;
04715       case 'n':
04716          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
04717             (*tries)++;
04718          else
04719             *tries = ao2_container_count(qe->parent->members);
04720          *noption = 1;
04721          break;
04722       case 'i':
04723          forwardsallowed = 0;
04724          break;
04725       case 'I':
04726          block_connected_line = 1;
04727          break;
04728       case 'x':
04729          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04730          break;
04731       case 'X':
04732          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04733          break;
04734       case 'C':
04735          qe->cancel_answered_elsewhere = 1;
04736          break;
04737       }
04738 
04739    /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 
04740       (this is mainly to support chan_local)
04741    */
04742    if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04743       qe->cancel_answered_elsewhere = 1;
04744    }
04745 
04746    ao2_lock(qe->parent);
04747    ast_debug(1, "%s is trying to call a queue member.\n",
04748                      qe->chan->name);
04749    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04750    if (!ast_strlen_zero(qe->announce))
04751       announce = qe->announce;
04752    if (!ast_strlen_zero(announceoverride))
04753       announce = announceoverride;
04754 
04755    memi = ao2_iterator_init(qe->parent->members, 0);
04756    while ((cur = ao2_iterator_next(&memi))) {
04757       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04758       struct ast_dialed_interface *di;
04759       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04760       if (!tmp) {
04761          ao2_ref(cur, -1);
04762          ao2_iterator_destroy(&memi);
04763          ao2_unlock(qe->parent);
04764          goto out;
04765       }
04766       if (!datastore) {
04767          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04768             callattempt_free(tmp);
04769             ao2_ref(cur, -1);
04770             ao2_iterator_destroy(&memi);
04771             ao2_unlock(qe->parent);
04772             goto out;
04773          }
04774          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04775          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04776             callattempt_free(tmp);
04777             ao2_ref(cur, -1);
04778             ao2_iterator_destroy(&memi);
04779             ao2_unlock(qe->parent);
04780             goto out;
04781          }
04782          datastore->data = dialed_interfaces;
04783          AST_LIST_HEAD_INIT(dialed_interfaces);
04784 
04785          ast_channel_lock(qe->chan);
04786          ast_channel_datastore_add(qe->chan, datastore);
04787          ast_channel_unlock(qe->chan);
04788       } else
04789          dialed_interfaces = datastore->data;
04790 
04791       AST_LIST_LOCK(dialed_interfaces);
04792       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04793          if (!strcasecmp(cur->interface, di->interface)) {
04794             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
04795                di->interface);
04796             break;
04797          }
04798       }
04799       AST_LIST_UNLOCK(dialed_interfaces);
04800 
04801       if (di) {
04802          callattempt_free(tmp);
04803          ao2_ref(cur, -1);
04804          continue;
04805       }
04806 
04807       /* It is always ok to dial a Local interface.  We only keep track of
04808        * which "real" interfaces have been dialed.  The Local channel will
04809        * inherit this list so that if it ends up dialing a real interface,
04810        * it won't call one that has already been called. */
04811       if (strncasecmp(cur->interface, "Local/", 6)) {
04812          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
04813             callattempt_free(tmp);
04814             ao2_ref(cur, -1);
04815             ao2_iterator_destroy(&memi);
04816             ao2_unlock(qe->parent);
04817             goto out;
04818          }
04819          strcpy(di->interface, cur->interface);
04820 
04821          AST_LIST_LOCK(dialed_interfaces);
04822          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
04823          AST_LIST_UNLOCK(dialed_interfaces);
04824       }
04825 
04826       /*
04827        * Seed the callattempt's connected line information with previously
04828        * acquired connected line info from the queued channel.  The
04829        * previously acquired connected line info could have been set
04830        * through the CONNECTED_LINE dialplan function.
04831        */
04832       ast_channel_lock(qe->chan);
04833       ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
04834       ast_channel_unlock(qe->chan);
04835 
04836       tmp->block_connected_update = block_connected_line;
04837       tmp->stillgoing = 1;
04838       tmp->member = cur;/* Place the reference for cur into callattempt. */
04839       tmp->lastcall = cur->lastcall;
04840       tmp->lastqueue = cur->lastqueue;
04841       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
04842       /* Special case: If we ring everyone, go ahead and ring them, otherwise
04843          just calculate their metric for the appropriate strategy */
04844       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
04845          /* Put them in the list of outgoing thingies...  We're ready now.
04846             XXX If we're forcibly removed, these outgoing calls won't get
04847             hung up XXX */
04848          tmp->q_next = outgoing;
04849          outgoing = tmp;      
04850          /* If this line is up, don't try anybody else */
04851          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
04852             break;
04853       } else {
04854          callattempt_free(tmp);
04855       }
04856    }
04857    ao2_iterator_destroy(&memi);
04858 
04859    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
04860       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
04861       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
04862          to = (qe->expire - now) * 1000;
04863       else
04864          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
04865    } else {
04866       /* Config timeout is higher priority thatn application timeout */
04867       if (qe->expire && qe->expire<=now) {
04868          to = 0;
04869       } else if (qe->parent->timeout) {
04870          to = qe->parent->timeout * 1000;
04871       } else {
04872          to = -1;
04873       }
04874    }
04875    orig = to;
04876    ++qe->pending;
04877    ao2_unlock(qe->parent);
04878    ring_one(qe, outgoing, &numbusies);
04879    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies,
04880       ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT),
04881       forwardsallowed, ringing);
04882    /* The ast_channel_datastore_remove() function could fail here if the
04883     * datastore was moved to another channel during a masquerade. If this is
04884     * the case, don't free the datastore here because later, when the channel
04885     * to which the datastore was moved hangs up, it will attempt to free this
04886     * datastore again, causing a crash
04887     */
04888    ast_channel_lock(qe->chan);
04889    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
04890       ast_datastore_free(datastore);
04891    }
04892    ast_channel_unlock(qe->chan);
04893    ao2_lock(qe->parent);
04894    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
04895       store_next_rr(qe, outgoing);
04896 
04897    }
04898    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
04899       store_next_lin(qe, outgoing);
04900    }
04901    ao2_unlock(qe->parent);
04902    peer = lpeer ? lpeer->chan : NULL;
04903    if (!peer) {
04904       qe->pending = 0;
04905       if (to) {
04906          /* Must gotten hung up */
04907          res = -1;
04908       } else {
04909          /* User exited by pressing a digit */
04910          res = digit;
04911       }
04912       if (res == -1)
04913          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
04914       if (ast_cdr_isset_unanswered()) {
04915          /* channel contains the name of one of the outgoing channels
04916             in its CDR; zero out this CDR to avoid a dual-posting */
04917          struct callattempt *o;
04918          for (o = outgoing; o; o = o->q_next) {
04919             if (!o->chan) {
04920                continue;
04921             }
04922             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
04923                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
04924                break;
04925             }
04926          }
04927       }
04928    } else { /* peer is valid */
04929       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
04930          we will always return with -1 so that it is hung up properly after the
04931          conversation.  */
04932       if (!strcmp(qe->chan->tech->type, "DAHDI"))
04933          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04934       if (!strcmp(peer->tech->type, "DAHDI"))
04935          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04936       /* Update parameters for the queue */
04937       time(&now);
04938       recalc_holdtime(qe, (now - qe->start));
04939       ao2_lock(qe->parent);
04940       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
04941       ao2_unlock(qe->parent);
04942       member = lpeer->member;
04943       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
04944       ao2_ref(member, 1);
04945       hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
04946       outgoing = NULL;
04947       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
04948          int res2;
04949 
04950          res2 = ast_autoservice_start(qe->chan);
04951          if (!res2) {
04952             if (qe->parent->memberdelay) {
04953                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
04954                res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
04955             }
04956             if (!res2 && announce) {
04957                if (play_file(peer, announce) < 0) {
04958                   ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name);
04959                }
04960             }
04961             if (!res2 && qe->parent->reportholdtime) {
04962                if (!play_file(peer, qe->parent->sound_reporthold)) {
04963                   int holdtime, holdtimesecs;
04964 
04965                   time(&now);
04966                   holdtime = abs((now - qe->start) / 60);
04967                   holdtimesecs = abs((now - qe->start) % 60);
04968                   if (holdtime > 0) {
04969                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
04970                      if (play_file(peer, qe->parent->sound_minutes) < 0) {
04971                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name);
04972                      }
04973                   }
04974                   if (holdtimesecs > 1) {
04975                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
04976                      if (play_file(peer, qe->parent->sound_seconds) < 0) {
04977                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name);
04978                      }
04979                   }
04980                }
04981             }
04982             ast_autoservice_stop(qe->chan);
04983          }
04984          if (ast_check_hangup(peer)) {
04985             /* Agent must have hung up */
04986             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
04987             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
04988             if (qe->parent->eventwhencalled)
04989                manager_event(EVENT_FLAG_AGENT, "AgentDump",
04990                      "Queue: %s\r\n"
04991                      "Uniqueid: %s\r\n"
04992                      "Channel: %s\r\n"
04993                      "Member: %s\r\n"
04994                      "MemberName: %s\r\n"
04995                      "%s",
04996                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04997                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04998             ast_hangup(peer);
04999             ao2_ref(member, -1);
05000             goto out;
05001          } else if (ast_check_hangup(qe->chan)) {
05002             /* Caller must have hung up just before being connected */
05003             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
05004             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
05005             record_abandoned(qe);
05006             ast_hangup(peer);
05007             ao2_ref(member, -1);
05008             return -1;
05009          }
05010       }
05011       /* Stop music on hold */
05012       if (ringing)
05013          ast_indicate(qe->chan,-1);
05014       else
05015          ast_moh_stop(qe->chan);
05016       /* If appropriate, log that we have a destination channel */
05017       if (qe->chan->cdr)
05018          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
05019       /* Make sure channels are compatible */
05020       res = ast_channel_make_compatible(qe->chan, peer);
05021       if (res < 0) {
05022          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
05023          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
05024          record_abandoned(qe);
05025          ast_cdr_failed(qe->chan->cdr);
05026          ast_hangup(peer);
05027          ao2_ref(member, -1);
05028          return -1;
05029       }
05030 
05031       /* Play announcement to the caller telling it's his turn if defined */
05032       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
05033          if (play_file(qe->chan, qe->parent->sound_callerannounce))
05034             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
05035       }
05036 
05037       ao2_lock(qe->parent);
05038       /* if setinterfacevar is defined, make member variables available to the channel */
05039       /* use  pbx_builtin_setvar to set a load of variables with one call */
05040       if (qe->parent->setinterfacevar) {
05041          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
05042             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
05043          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
05044          pbx_builtin_setvar_multiple(peer, interfacevar);
05045       }
05046       
05047       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
05048       /* use  pbx_builtin_setvar to set a load of variables with one call */
05049       if (qe->parent->setqueueentryvar) {
05050          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
05051             (long) time(NULL) - qe->start, qe->opos);
05052          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
05053          pbx_builtin_setvar_multiple(peer, interfacevar);
05054       }
05055    
05056       ao2_unlock(qe->parent);
05057 
05058       /* try to set queue variables if configured to do so*/
05059       set_queue_variables(qe->parent, qe->chan);
05060       set_queue_variables(qe->parent, peer);
05061       
05062       ast_channel_lock(qe->chan);
05063       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
05064             monitorfilename = ast_strdupa(monitorfilename);
05065       }
05066       ast_channel_unlock(qe->chan);
05067       /* Begin Monitoring */
05068       if (qe->parent->monfmt && *qe->parent->monfmt) {
05069          if (!qe->parent->montype) {
05070             const char *monexec;
05071             ast_debug(1, "Starting Monitor as requested.\n");
05072             ast_channel_lock(qe->chan);
05073             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
05074                which = qe->chan;
05075                monexec = monexec ? ast_strdupa(monexec) : NULL;
05076             }
05077             else
05078                which = peer;
05079             ast_channel_unlock(qe->chan);
05080             if (monitorfilename) {
05081                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
05082             } else if (qe->chan->cdr) {
05083                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
05084             } else {
05085                /* Last ditch effort -- no CDR, make up something */
05086                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
05087                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
05088             }
05089             if (!ast_strlen_zero(monexec)) {
05090                ast_monitor_setjoinfiles(which, 1);
05091             }
05092          } else {
05093             mixmonapp = pbx_findapp("MixMonitor");
05094             
05095             if (mixmonapp) {
05096                ast_debug(1, "Starting MixMonitor as requested.\n");
05097                if (!monitorfilename) {
05098                   if (qe->chan->cdr)
05099                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
05100                   else
05101                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
05102                } else {
05103                   const char *m = monitorfilename;
05104                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
05105                      switch (*m) {
05106                      case '^':
05107                         if (*(m + 1) == '{')
05108                            *p = '$';
05109                         break;
05110                      case ',':
05111                         *p++ = '\\';
05112                         /* Fall through */
05113                      default:
05114                         *p = *m;
05115                      }
05116                      if (*m == '\0')
05117                         break;
05118                   }
05119                   if (p == tmpid2 + sizeof(tmpid2))
05120                      tmpid2[sizeof(tmpid2) - 1] = '\0';
05121 
05122                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
05123                }
05124 
05125                ast_channel_lock(qe->chan);
05126                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
05127                      monitor_exec = ast_strdupa(monitor_exec);
05128                }
05129                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
05130                      monitor_options = ast_strdupa(monitor_options);
05131                } else {
05132                   monitor_options = "";
05133                }
05134                ast_channel_unlock(qe->chan);
05135 
05136                if (monitor_exec) {
05137                   const char *m = monitor_exec;
05138                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
05139                      switch (*m) {
05140                      case '^':
05141                         if (*(m + 1) == '{')
05142                            *p = '$';
05143                         break;
05144                      case ',':
05145                         *p++ = '\\';
05146                         /* Fall through */
05147                      default:
05148                         *p = *m;
05149                      }
05150                      if (*m == '\0')
05151                         break;
05152                   }
05153                   if (p == meid2 + sizeof(meid2))
05154                      meid2[sizeof(meid2) - 1] = '\0';
05155 
05156                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
05157                }
05158    
05159                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
05160 
05161                if (!ast_strlen_zero(monitor_exec))
05162                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
05163                else
05164                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
05165                
05166                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
05167                /* We purposely lock the CDR so that pbx_exec does not update the application data */
05168                if (qe->chan->cdr)
05169                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
05170                pbx_exec(qe->chan, mixmonapp, mixmonargs);
05171                if (qe->chan->cdr)
05172                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
05173 
05174             } else {
05175                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
05176             }
05177          }
05178       }
05179       /* Drop out of the queue at this point, to prepare for next caller */
05180       leave_queue(qe);        
05181       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
05182          ast_debug(1, "app_queue: sendurl=%s.\n", url);
05183          ast_channel_sendurl(peer, url);
05184       }
05185       
05186       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
05187       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
05188       if (!ast_strlen_zero(macro)) {
05189             macroexec = ast_strdupa(macro);
05190       } else {
05191          if (qe->parent->membermacro)
05192             macroexec = ast_strdupa(qe->parent->membermacro);
05193       }
05194 
05195       if (!ast_strlen_zero(macroexec)) {
05196          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
05197          
05198          res = ast_autoservice_start(qe->chan);
05199          if (res) {
05200             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
05201             res = -1;
05202          }
05203          
05204          application = pbx_findapp("Macro");
05205 
05206          if (application) {
05207             res = pbx_exec(peer, application, macroexec);
05208             ast_debug(1, "Macro exited with status %d\n", res);
05209             res = 0;
05210          } else {
05211             ast_log(LOG_ERROR, "Could not find application Macro\n");
05212             res = -1;
05213          }
05214 
05215          if (ast_autoservice_stop(qe->chan) < 0) {
05216             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05217             res = -1;
05218          }
05219       }
05220 
05221       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
05222       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
05223       if (!ast_strlen_zero(gosub)) {
05224             gosubexec = ast_strdupa(gosub);
05225       } else {
05226          if (qe->parent->membergosub)
05227             gosubexec = ast_strdupa(qe->parent->membergosub);
05228       }
05229 
05230       if (!ast_strlen_zero(gosubexec)) {
05231          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
05232          
05233          res = ast_autoservice_start(qe->chan);
05234          if (res) {
05235             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
05236             res = -1;
05237          }
05238          
05239          application = pbx_findapp("Gosub");
05240          
05241          if (application) {
05242             char *gosub_args, *gosub_argstart;
05243 
05244             /* Set where we came from */
05245             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
05246             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
05247             peer->priority = 0;
05248 
05249             gosub_argstart = strchr(gosubexec, ',');
05250             if (gosub_argstart) {
05251                const char *what_is_s = "s";
05252                *gosub_argstart = 0;
05253                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05254                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05255                   what_is_s = "~~s~~";
05256                }
05257                if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
05258                   gosub_args = NULL;
05259                }
05260                *gosub_argstart = ',';
05261             } else {
05262                const char *what_is_s = "s";
05263                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05264                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05265                   what_is_s = "~~s~~";
05266                }
05267                if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
05268                   gosub_args = NULL;
05269                }
05270             }
05271             if (gosub_args) {
05272                res = pbx_exec(peer, application, gosub_args);
05273                if (!res) {
05274                   struct ast_pbx_args args;
05275                   memset(&args, 0, sizeof(args));
05276                   args.no_hangup_chan = 1;
05277                   ast_pbx_run_args(peer, &args);
05278                }
05279                ast_free(gosub_args);
05280                ast_debug(1, "Gosub exited with status %d\n", res);
05281             } else {
05282                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
05283             }
05284          } else {
05285             ast_log(LOG_ERROR, "Could not find application Gosub\n");
05286             res = -1;
05287          }
05288       
05289          if (ast_autoservice_stop(qe->chan) < 0) {
05290             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05291             res = -1;
05292          }
05293       }
05294 
05295       if (!ast_strlen_zero(agi)) {
05296          ast_debug(1, "app_queue: agi=%s.\n", agi);
05297          application = pbx_findapp("agi");
05298          if (application) {
05299             agiexec = ast_strdupa(agi);
05300             pbx_exec(qe->chan, application, agiexec);
05301          } else
05302             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
05303       }
05304       qe->handled++;
05305       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
05306                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
05307 
05308       if (qe->chan->cdr) {
05309          struct ast_cdr *cdr;
05310          struct ast_cdr *newcdr;
05311 
05312          /* Only work with the last CDR in the stack*/
05313          cdr = qe->chan->cdr;
05314          while (cdr->next) {
05315             cdr = cdr->next;
05316          }
05317 
05318          /* If this CDR is not related to us add new one*/
05319          if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) &&
05320              (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) &&
05321              (newcdr = ast_cdr_dup(cdr))) {
05322             ast_channel_lock(qe->chan);
05323             ast_cdr_init(newcdr, qe->chan);
05324             ast_cdr_reset(newcdr, 0);
05325             cdr = ast_cdr_append(cdr, newcdr);
05326             cdr = cdr->next;
05327             ast_channel_unlock(qe->chan);
05328          }
05329 
05330          if (update_cdr) {
05331             ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
05332          }
05333       }
05334 
05335       if (qe->parent->eventwhencalled)
05336          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
05337                "Queue: %s\r\n"
05338                "Uniqueid: %s\r\n"
05339                "Channel: %s\r\n"
05340                "Member: %s\r\n"
05341                "MemberName: %s\r\n"
05342                "Holdtime: %ld\r\n"
05343                "BridgedChannel: %s\r\n"
05344                "Ringtime: %ld\r\n"
05345                "%s",
05346                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
05347                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
05348                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05349       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
05350       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
05351    
05352       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
05353          queue_end_bridge->q = qe->parent;
05354          queue_end_bridge->chan = qe->chan;
05355          bridge_config.end_bridge_callback = end_bridge_callback;
05356          bridge_config.end_bridge_callback_data = queue_end_bridge;
05357          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
05358          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
05359           * to make sure to increase the refcount of this queue so it cannot be freed until we
05360           * are done with it. We remove this reference in end_bridge_callback.
05361           */
05362          queue_t_ref(qe->parent, "For bridge_config reference");
05363       }
05364 
05365       time(&callstart);
05366       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
05367       bridge = ast_bridge_call(qe->chan, peer, &bridge_config);
05368 
05369       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
05370        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
05371        * the AgentComplete manager event
05372        */
05373       ast_channel_lock(qe->chan);
05374       if (!attended_transfer_occurred(qe->chan)) {
05375          struct ast_datastore *tds;
05376 
05377          /* detect a blind transfer */
05378          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
05379             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
05380                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
05381                (long) (time(NULL) - callstart), qe->opos);
05382             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05383          } else if (ast_check_hangup(qe->chan) && !ast_check_hangup(peer)) {
05384             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
05385                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05386             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
05387          } else {
05388             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
05389                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05390             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
05391          }
05392          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
05393             ast_channel_datastore_remove(qe->chan, tds);
05394          }
05395          ast_channel_unlock(qe->chan);
05396          update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
05397       } else {
05398          ast_channel_unlock(qe->chan);
05399 
05400          /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
05401          send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05402       }
05403 
05404       if (transfer_ds) {
05405          ast_datastore_free(transfer_ds);
05406       }
05407       ast_hangup(peer);
05408       res = bridge ? bridge : 1;
05409       ao2_ref(member, -1);
05410    }
05411 out:
05412    hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
05413 
05414    return res;
05415 }

static int unload_module ( void   )  [static]

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

08726 {
08727    int res;
08728    struct ast_context *con;
08729    struct ao2_iterator q_iter;
08730    struct call_queue *q = NULL;
08731 
08732    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08733    res = ast_manager_unregister("QueueStatus");
08734    res |= ast_manager_unregister("Queues");
08735    res |= ast_manager_unregister("QueueRule");
08736    res |= ast_manager_unregister("QueueSummary");
08737    res |= ast_manager_unregister("QueueAdd");
08738    res |= ast_manager_unregister("QueueRemove");
08739    res |= ast_manager_unregister("QueuePause");
08740    res |= ast_manager_unregister("QueueLog");
08741    res |= ast_manager_unregister("QueuePenalty");
08742    res |= ast_manager_unregister("QueueReload");
08743    res |= ast_manager_unregister("QueueReset");
08744    res |= ast_unregister_application(app_aqm);
08745    res |= ast_unregister_application(app_rqm);
08746    res |= ast_unregister_application(app_pqm);
08747    res |= ast_unregister_application(app_upqm);
08748    res |= ast_unregister_application(app_ql);
08749    res |= ast_unregister_application(app);
08750    res |= ast_custom_function_unregister(&queueexists_function);
08751    res |= ast_custom_function_unregister(&queuevar_function);
08752    res |= ast_custom_function_unregister(&queuemembercount_function);
08753    res |= ast_custom_function_unregister(&queuemembercount_dep);
08754    res |= ast_custom_function_unregister(&queuememberlist_function);
08755    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08756    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08757 
08758    res |= ast_data_unregister(NULL);
08759 
08760    if (device_state_sub)
08761       ast_event_unsubscribe(device_state_sub);
08762 
08763    ast_extension_state_del(0, extension_state_cb);
08764 
08765    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08766       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08767       ast_context_destroy(con, "app_queue"); /* leave no trace */
08768    }
08769 
08770    q_iter = ao2_iterator_init(queues, 0);
08771    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08772       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08773       queue_t_unref(q, "Done with iterator");
08774    }
08775    ao2_iterator_destroy(&q_iter);
08776    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08777    ao2_ref(queues, -1);
08778    ast_unload_realtime("queue_members");
08779    return res;
08780 }

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

04175 {
04176    int max_penalty = INT_MAX;
04177 
04178    if (qe->max_penalty != INT_MAX) {
04179       char max_penalty_str[20];
04180 
04181       if (qe->pr->max_relative) {
04182          max_penalty = qe->max_penalty + qe->pr->max_value;
04183       } else {
04184          max_penalty = qe->pr->max_value;
04185       }
04186 
04187       /* a relative change to the penalty could put it below 0 */
04188       if (max_penalty < 0) {
04189          max_penalty = 0;
04190       }
04191 
04192       snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
04193       pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
04194       qe->max_penalty = max_penalty;
04195       ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
04196          qe->max_penalty, qe->chan->name, qe->pr->time);
04197    }
04198 
04199    if (qe->min_penalty != INT_MAX) {
04200       char min_penalty_str[20];
04201       int min_penalty;
04202 
04203       if (qe->pr->min_relative) {
04204          min_penalty = qe->min_penalty + qe->pr->min_value;
04205       } else {
04206          min_penalty = qe->pr->min_value;
04207       }
04208 
04209       if (min_penalty < 0) {
04210          min_penalty = 0;
04211       }
04212 
04213       if (max_penalty != INT_MAX && min_penalty > max_penalty) {
04214          min_penalty = max_penalty;
04215       }
04216 
04217       snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
04218       pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
04219       qe->min_penalty = min_penalty;
04220       ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
04221          qe->min_penalty, qe->chan->name, qe->pr->time);
04222    }
04223 
04224    qe->pr = AST_LIST_NEXT(qe->pr, list);
04225 }

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

04314 {
04315    int oldtalktime;
04316 
04317    struct member *mem;
04318    struct call_queue *qtmp;
04319    struct ao2_iterator queue_iter;  
04320    
04321    if (shared_lastcall) {
04322       queue_iter = ao2_iterator_init(queues, 0);
04323       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04324          ao2_lock(qtmp);
04325          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04326             time(&mem->lastcall);
04327             mem->calls++;
04328             mem->lastqueue = q;
04329             ao2_ref(mem, -1);
04330          }
04331          ao2_unlock(qtmp);
04332          queue_t_unref(qtmp, "Done with iterator");
04333       }
04334       ao2_iterator_destroy(&queue_iter);
04335    } else {
04336       ao2_lock(q);
04337       time(&member->lastcall);
04338       member->calls++;
04339       member->lastqueue = q;
04340       ao2_unlock(q);
04341    }  
04342    ao2_lock(q);
04343    q->callscompleted++;
04344    if (callcompletedinsl)
04345       q->callscompletedinsl++;
04346    /* Calculate talktime using the same exponential average as holdtime code*/
04347    oldtalktime = q->talktime;
04348    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04349    ao2_unlock(q);
04350    return 0;
04351 }

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

Definition at line 2475 of file app_queue.c.

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

Referenced by set_member_paused().

02476 {
02477    int ret = -1;
02478 
02479    if (ast_strlen_zero(mem->rt_uniqueid))
02480       return ret;
02481 
02482    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02483       ret = 0;
02484 
02485    return ret;
02486 }

static void update_realtime_members ( struct call_queue q  )  [static]

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

02490 {
02491    struct ast_config *member_config = NULL;
02492    struct member *m;
02493    char *interface = NULL;
02494    struct ao2_iterator mem_iter;
02495 
02496    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02497       /* This queue doesn't have realtime members. If the queue still has any realtime
02498        * members in memory, they need to be removed.
02499        */
02500       ao2_lock(q);
02501       mem_iter = ao2_iterator_init(q->members, 0);
02502       while ((m = ao2_iterator_next(&mem_iter))) {
02503          if (m->realtime) {
02504             member_remove_from_queue(q, m);
02505          }
02506          ao2_ref(m, -1);
02507       }
02508       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02509       ao2_unlock(q);
02510       return;
02511    }
02512 
02513    ao2_lock(q);
02514 
02515    /* Temporarily set realtime  members dead so we can detect deleted ones.*/
02516    mem_iter = ao2_iterator_init(q->members, 0);
02517    while ((m = ao2_iterator_next(&mem_iter))) {
02518       if (m->realtime)
02519          m->dead = 1;
02520       ao2_ref(m, -1);
02521    }
02522    ao2_iterator_destroy(&mem_iter);
02523 
02524    while ((interface = ast_category_browse(member_config, interface))) {
02525       rt_handle_member_record(q, interface,
02526          ast_variable_retrieve(member_config, interface, "uniqueid"),
02527          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02528          ast_variable_retrieve(member_config, interface, "penalty"),
02529          ast_variable_retrieve(member_config, interface, "paused"),
02530          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02531    }
02532 
02533    /* Delete all realtime members that have been deleted in DB. */
02534    mem_iter = ao2_iterator_init(q->members, 0);
02535    while ((m = ao2_iterator_next(&mem_iter))) {
02536       if (m->dead) {
02537          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02538          member_remove_from_queue(q, m);
02539       }
02540       ao2_ref(m, -1);
02541    }
02542    ao2_iterator_destroy(&mem_iter);
02543    ao2_unlock(q);
02544    ast_config_destroy(member_config);
02545 }

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

01483 {
01484    m->status = status;
01485 
01486    if (q->maskmemberstatus)
01487       return 0;
01488 
01489    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01490       "Queue: %s\r\n"
01491       "Location: %s\r\n"
01492       "MemberName: %s\r\n"
01493       "Membership: %s\r\n"
01494       "Penalty: %d\r\n"
01495       "CallsTaken: %d\r\n"
01496       "LastCall: %d\r\n"
01497       "Status: %d\r\n"
01498       "Paused: %d\r\n",
01499       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01500       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01501    );
01502 
01503    return 0;
01504 }

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

UnPauseQueueMember application.

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

05896 {
05897    char *parse;
05898    AST_DECLARE_APP_ARGS(args,
05899       AST_APP_ARG(queuename);
05900       AST_APP_ARG(interface);
05901       AST_APP_ARG(options);
05902       AST_APP_ARG(reason);
05903    );
05904 
05905    if (ast_strlen_zero(data)) {
05906       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05907       return -1;
05908    }
05909 
05910    parse = ast_strdupa(data);
05911 
05912    AST_STANDARD_APP_ARGS(args, parse);
05913 
05914    if (ast_strlen_zero(args.interface)) {
05915       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05916       return -1;
05917    }
05918 
05919    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05920       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05921       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05922       return 0;
05923    }
05924 
05925    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05926 
05927    return 0;
05928 }

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

02663 {
02664    int digitlen = strlen(qe->digits);
02665 
02666    /* Prevent possible buffer overflow */
02667    if (digitlen < sizeof(qe->digits) - 2) {
02668       qe->digits[digitlen] = digit;
02669       qe->digits[digitlen + 1] = '\0';
02670    } else {
02671       qe->digits[0] = '\0';
02672       return 0;
02673    }
02674 
02675    /* If there's no context to goto, short-circuit */
02676    if (ast_strlen_zero(qe->context))
02677       return 0;
02678 
02679    /* If the extension is bad, then reset the digits to blank */
02680    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02681       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02682       qe->digits[0] = '\0';
02683       return 0;
02684    }
02685 
02686    /* We have an exact match */
02687    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02688       qe->valid_digits = 1;
02689       /* Return 1 on a successful goto */
02690       return 1;
02691    }
02692 
02693    return 0;
02694 }

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

03051 {
03052    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
03053    const char *tmp;
03054 
03055    if (pbx_builtin_serialize_variables(chan, &buf)) {
03056       int i, j;
03057 
03058       /* convert "\n" to "\nVariable: " */
03059       strcpy(vars, "Variable: ");
03060       tmp = ast_str_buffer(buf);
03061 
03062       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
03063          vars[j] = tmp[i];
03064 
03065          if (tmp[i + 1] == '\0')
03066             break;
03067          if (tmp[i] == '\n') {
03068             vars[j++] = '\r';
03069             vars[j++] = '\n';
03070 
03071             ast_copy_string(&(vars[j]), "Variable: ", len - j);
03072             j += 9;
03073          }
03074       }
03075       if (j > len - 3)
03076          j = len - 3;
03077       vars[j++] = '\r';
03078       vars[j++] = '\n';
03079       vars[j] = '\0';
03080    } else {
03081       /* there are no channel variables; leave it blank */
03082       *vars = '\0';
03083    }
03084    return vars;
03085 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5417 of file app_queue.c.

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

Referenced by queue_exec().

05418 {
05419    /* Don't need to hold the lock while we setup the outgoing calls */
05420    int retrywait = qe->parent->retry * 1000;
05421 
05422    int res = ast_waitfordigit(qe->chan, retrywait);
05423    if (res > 0 && !valid_exit(qe, res))
05424       res = 0;
05425 
05426    return res;
05427 }

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 3614 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_busy(), 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_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().

03615 {
03616    const char *queue = qe->parent->name;
03617    struct callattempt *o, *start = NULL, *prev = NULL;
03618    int status;
03619    int numbusies = prebusies;
03620    int numnochan = 0;
03621    int stillgoing = 0;
03622    int orig = *to;
03623    struct ast_frame *f;
03624    struct callattempt *peer = NULL;
03625    struct ast_channel *winner;
03626    struct ast_channel *in = qe->chan;
03627    char on[80] = "";
03628    char membername[80] = "";
03629    long starttime = 0;
03630    long endtime = 0;
03631 #ifdef HAVE_EPOLL
03632    struct callattempt *epollo;
03633 #endif
03634    struct ast_party_connected_line connected_caller;
03635    char *inchan_name;
03636    struct timeval start_time_tv = ast_tvnow();
03637 
03638    ast_party_connected_line_init(&connected_caller);
03639 
03640    ast_channel_lock(qe->chan);
03641    inchan_name = ast_strdupa(qe->chan->name);
03642    ast_channel_unlock(qe->chan);
03643 
03644    starttime = (long) time(NULL);
03645 #ifdef HAVE_EPOLL
03646    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03647       if (epollo->chan)
03648          ast_poll_channel_add(in, epollo->chan);
03649    }
03650 #endif
03651 
03652    while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) {
03653       int numlines, retry, pos = 1;
03654       struct ast_channel *watchers[AST_MAX_WATCHERS];
03655       watchers[0] = in;
03656       start = NULL;
03657 
03658       for (retry = 0; retry < 2; retry++) {
03659          numlines = 0;
03660          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
03661             if (o->stillgoing) { /* Keep track of important channels */
03662                stillgoing = 1;
03663                if (o->chan) {
03664                   if (pos < AST_MAX_WATCHERS) {
03665                      watchers[pos++] = o->chan;
03666                   }
03667                   if (!start)
03668                      start = o;
03669                   else
03670                      prev->call_next = o;
03671                   prev = o;
03672                }
03673             }
03674             numlines++;
03675          }
03676          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
03677             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
03678             break;
03679          /* On "ringall" strategy we only move to the next penalty level
03680             when *all* ringing phones are done in the current penalty level */
03681          ring_one(qe, outgoing, &numbusies);
03682          /* and retry... */
03683       }
03684       if (pos == 1 /* not found */) {
03685          if (numlines == (numbusies + numnochan)) {
03686             ast_debug(1, "Everyone is busy at this time\n");
03687          } else {
03688             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03689          }
03690          *to = 0;
03691          return NULL;
03692       }
03693 
03694       /* Poll for events from both the incoming channel as well as any outgoing channels */
03695       winner = ast_waitfor_n(watchers, pos, to);
03696 
03697       /* Service all of the outgoing channels */
03698       for (o = start; o; o = o->call_next) {
03699          /* We go with a fixed buffer here instead of using ast_strdupa. Using
03700           * ast_strdupa in a loop like this one can cause a stack overflow
03701           */
03702          char ochan_name[AST_CHANNEL_NAME];
03703 
03704          if (o->chan) {
03705             ast_channel_lock(o->chan);
03706             ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name));
03707             ast_channel_unlock(o->chan);
03708          }
03709          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
03710             if (!peer) {
03711                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03712                if (!o->block_connected_update) {
03713                   if (o->pending_connected_update) {
03714                      if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03715                         ast_channel_update_connected_line(in, &o->connected, NULL);
03716                      }
03717                   } else if (!o->dial_callerid_absent) {
03718                      ast_channel_lock(o->chan);
03719                      ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03720                      ast_channel_unlock(o->chan);
03721                      connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03722                      if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03723                         ast_channel_update_connected_line(in, &connected_caller, NULL);
03724                      }
03725                      ast_party_connected_line_free(&connected_caller);
03726                   }
03727                }
03728                if (o->aoc_s_rate_list) {
03729                   size_t encoded_size;
03730                   struct ast_aoc_encoded *encoded;
03731                   if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03732                      ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03733                      ast_aoc_destroy_encoded(encoded);
03734                   }
03735                }
03736                peer = o;
03737             }
03738          } else if (o->chan && (o->chan == winner)) {
03739 
03740             ast_copy_string(on, o->member->interface, sizeof(on));
03741             ast_copy_string(membername, o->member->membername, sizeof(membername));
03742 
03743             /* Before processing channel, go ahead and check for forwarding */
03744             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03745                ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03746                numnochan++;
03747                do_hang(o);
03748                winner = NULL;
03749                continue;
03750             } else if (!ast_strlen_zero(o->chan->call_forward)) {
03751                struct ast_channel *original = o->chan;
03752                char tmpchan[256];
03753                char *stuff;
03754                char *tech;
03755 
03756                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03757                if ((stuff = strchr(tmpchan, '/'))) {
03758                   *stuff++ = '\0';
03759                   tech = tmpchan;
03760                } else {
03761                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03762                   stuff = tmpchan;
03763                   tech = "Local";
03764                }
03765                if (!strcasecmp(tech, "Local")) {
03766                   /*
03767                    * Drop the connected line update block for local channels since
03768                    * this is going to run dialplan and the user can change his
03769                    * mind about what connected line information he wants to send.
03770                    */
03771                   o->block_connected_update = 0;
03772                }
03773 
03774                ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03775 
03776                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03777                /* Setup parameters */
03778                o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03779                if (!o->chan) {
03780                   ast_log(LOG_NOTICE,
03781                      "Forwarding failed to create channel to dial '%s/%s'\n",
03782                      tech, stuff);
03783                   o->stillgoing = 0;
03784                   numnochan++;
03785                } else {
03786                   ast_channel_lock_both(o->chan, original);
03787                   ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting);
03788                   ast_channel_unlock(o->chan);
03789                   ast_channel_unlock(original);
03790 
03791                   ast_channel_lock_both(o->chan, in);
03792                   ast_channel_inherit_variables(in, o->chan);
03793                   ast_channel_datastore_inherit(in, o->chan);
03794 
03795                   if (o->pending_connected_update) {
03796                      /*
03797                       * Re-seed the callattempt's connected line information with
03798                       * previously acquired connected line info from the queued
03799                       * channel.  The previously acquired connected line info could
03800                       * have been set through the CONNECTED_LINE dialplan function.
03801                       */
03802                      o->pending_connected_update = 0;
03803                      ast_party_connected_line_copy(&o->connected, &in->connected);
03804                   }
03805 
03806                   ast_string_field_set(o->chan, accountcode, in->accountcode);
03807 
03808                   if (!o->chan->redirecting.from.number.valid
03809                      || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03810                      /*
03811                       * The call was not previously redirected so it is
03812                       * now redirected from this number.
03813                       */
03814                      ast_party_number_free(&o->chan->redirecting.from.number);
03815                      ast_party_number_init(&o->chan->redirecting.from.number);
03816                      o->chan->redirecting.from.number.valid = 1;
03817                      o->chan->redirecting.from.number.str =
03818                         ast_strdup(S_OR(in->macroexten, in->exten));
03819                   }
03820 
03821                   o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03822 
03823                   o->dial_callerid_absent = !o->chan->caller.id.number.valid
03824                      || ast_strlen_zero(o->chan->caller.id.number.str);
03825                   ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller);
03826 
03827                   ast_channel_unlock(in);
03828                   if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL
03829                      && !o->block_connected_update) {
03830                      struct ast_party_redirecting redirecting;
03831 
03832                      /*
03833                       * Redirecting updates to the caller make sense only on single
03834                       * call at a time strategies.
03835                       *
03836                       * We must unlock o->chan before calling
03837                       * ast_channel_redirecting_macro, because we put o->chan into
03838                       * autoservice there.  That is pretty much a guaranteed
03839                       * deadlock.  This is why the handling of o->chan's lock may
03840                       * seem a bit unusual here.
03841                       */
03842                      ast_party_redirecting_init(&redirecting);
03843                      ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03844                      ast_channel_unlock(o->chan);
03845                      if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) {
03846                         ast_channel_update_redirecting(in, &redirecting, NULL);
03847                      }
03848                      ast_party_redirecting_free(&redirecting);
03849                   } else {
03850                      ast_channel_unlock(o->chan);
03851                   }
03852 
03853                   if (ast_call(o->chan, stuff, 0)) {
03854                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03855                         tech, stuff);
03856                      do_hang(o);
03857                      numnochan++;
03858                   }
03859                }
03860                /* Hangup the original channel now, in case we needed it */
03861                ast_hangup(winner);
03862                continue;
03863             }
03864             f = ast_read(winner);
03865             if (f) {
03866                if (f->frametype == AST_FRAME_CONTROL) {
03867                   switch (f->subclass.integer) {
03868                   case AST_CONTROL_ANSWER:
03869                      /* This is our guy if someone answered. */
03870                      if (!peer) {
03871                         ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03872                         if (!o->block_connected_update) {
03873                            if (o->pending_connected_update) {
03874                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03875                                  ast_channel_update_connected_line(in, &o->connected, NULL);
03876                               }
03877                            } else if (!o->dial_callerid_absent) {
03878                               ast_channel_lock(o->chan);
03879                               ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03880                               ast_channel_unlock(o->chan);
03881                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03882                               if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) {
03883                                  ast_channel_update_connected_line(in, &connected_caller, NULL);
03884                               }
03885                               ast_party_connected_line_free(&connected_caller);
03886                            }
03887                         }
03888                         if (o->aoc_s_rate_list) {
03889                            size_t encoded_size;
03890                            struct ast_aoc_encoded *encoded;
03891                            if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03892                               ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03893                               ast_aoc_destroy_encoded(encoded);
03894                            }
03895                         }
03896                         peer = o;
03897                      }
03898                      break;
03899                   case AST_CONTROL_BUSY:
03900                      ast_verb(3, "%s is busy\n", ochan_name);
03901                      if (in->cdr)
03902                         ast_cdr_busy(in->cdr);
03903                      do_hang(o);
03904                      endtime = (long) time(NULL);
03905                      endtime -= starttime;
03906                      rna(endtime * 1000, qe, on, membername, 0);
03907                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03908                         if (qe->parent->timeoutrestart) {
03909                            start_time_tv = ast_tvnow();
03910                         }
03911                         /* Have enough time for a queue member to answer? */
03912                         if (ast_remaining_ms(start_time_tv, orig) > 500) {
03913                            ring_one(qe, outgoing, &numbusies);
03914                            starttime = (long) time(NULL);
03915                         }
03916                      }
03917                      numbusies++;
03918                      break;
03919                   case AST_CONTROL_CONGESTION:
03920                      ast_verb(3, "%s is circuit-busy\n", ochan_name);
03921                      if (in->cdr)
03922                         ast_cdr_busy(in->cdr);
03923                      endtime = (long) time(NULL);
03924                      endtime -= starttime;
03925                      rna(endtime * 1000, qe, on, membername, 0);
03926                      do_hang(o);
03927                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03928                         if (qe->parent->timeoutrestart) {
03929                            start_time_tv = ast_tvnow();
03930                         }
03931                         if (ast_remaining_ms(start_time_tv, orig) > 500) {
03932                            ring_one(qe, outgoing, &numbusies);
03933                            starttime = (long) time(NULL);
03934                         }
03935                      }
03936                      numbusies++;
03937                      break;
03938                   case AST_CONTROL_RINGING:
03939                      ast_verb(3, "%s is ringing\n", ochan_name);
03940 
03941                      /* Start ring indication when the channel is ringing, if specified */
03942                      if (qe->ring_when_ringing) {
03943                         ast_moh_stop(qe->chan);
03944                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
03945                      }
03946                      break;
03947                   case AST_CONTROL_OFFHOOK:
03948                      /* Ignore going off hook */
03949                      break;
03950                   case AST_CONTROL_CONNECTED_LINE:
03951                      if (o->block_connected_update) {
03952                         ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03953                         break;
03954                      }
03955                      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03956                         struct ast_party_connected_line connected;
03957 
03958                         ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03959                         ast_party_connected_line_set_init(&connected, &o->connected);
03960                         ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03961                         ast_party_connected_line_set(&o->connected, &connected, NULL);
03962                         ast_party_connected_line_free(&connected);
03963                         o->pending_connected_update = 1;
03964                         break;
03965                      }
03966 
03967                      /*
03968                       * Prevent using the CallerID from the outgoing channel since we
03969                       * got a connected line update from it.
03970                       */
03971                      o->dial_callerid_absent = 1;
03972 
03973                      if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03974                         ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03975                      }
03976                      break;
03977                   case AST_CONTROL_AOC:
03978                      {
03979                         struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03980                         if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03981                            ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03982                            o->aoc_s_rate_list = decoded;
03983                         } else {
03984                            ast_aoc_destroy_decoded(decoded);
03985                         }
03986                      }
03987                      break;
03988                   case AST_CONTROL_REDIRECTING:
03989                      if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03990                         /*
03991                          * Redirecting updates to the caller make sense only on single
03992                          * call at a time strategies.
03993                          */
03994                         break;
03995                      }
03996                      if (o->block_connected_update) {
03997                         ast_verb(3, "Redirecting update to %s prevented\n",
03998                            inchan_name);
03999                         break;
04000                      }
04001                      ast_verb(3, "%s redirecting info has changed, passing it to %s\n",
04002                         ochan_name, inchan_name);
04003                      if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
04004                         ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
04005                      }
04006                      break;
04007                   default:
04008                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
04009                      break;
04010                   }
04011                }
04012                ast_frfree(f);
04013             } else { /* ast_read() returned NULL */
04014                endtime = (long) time(NULL) - starttime;
04015                rna(endtime * 1000, qe, on, membername, 1);
04016                do_hang(o);
04017                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
04018                   if (qe->parent->timeoutrestart) {
04019                      start_time_tv = ast_tvnow();
04020                   }
04021                   if (ast_remaining_ms(start_time_tv, orig) > 500) {
04022                      ring_one(qe, outgoing, &numbusies);
04023                      starttime = (long) time(NULL);
04024                   }
04025                }
04026             }
04027          }
04028       }
04029 
04030       /* If we received an event from the caller, deal with it. */
04031       if (winner == in) {
04032          f = ast_read(in);
04033          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
04034             /* Got hung up */
04035             *to = -1;
04036             if (f) {
04037                if (f->data.uint32) {
04038                   in->hangupcause = f->data.uint32;
04039                }
04040                ast_frfree(f);
04041             }
04042             return NULL;
04043          }
04044 
04045          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
04046             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
04047             *to = 0;
04048             ast_frfree(f);
04049             return NULL;
04050          }
04051          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
04052             ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
04053             *to = 0;
04054             *digit = f->subclass.integer;
04055             ast_frfree(f);
04056             return NULL;
04057          }
04058 
04059          /* Send the frame from the in channel to all outgoing channels. */
04060          for (o = start; o; o = o->call_next) {
04061             if (!o->stillgoing || !o->chan) {
04062                /* This outgoing channel has died so don't send the frame to it. */
04063                continue;
04064             }
04065             switch (f->frametype) {
04066             case AST_FRAME_CONTROL:
04067                switch (f->subclass.integer) {
04068                case AST_CONTROL_CONNECTED_LINE:
04069                   if (ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) {
04070                      ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
04071                   }
04072                   break;
04073                case AST_CONTROL_REDIRECTING:
04074                   if (ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) {
04075                      ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen);
04076                   }
04077                   break;
04078                default:
04079                   /* We are not going to do anything with this frame. */
04080                   goto skip_frame;
04081                }
04082                break;
04083             default:
04084                /* We are not going to do anything with this frame. */
04085                goto skip_frame;
04086             }
04087          }
04088 skip_frame:;
04089 
04090          ast_frfree(f);
04091       }
04092    }
04093 
04094    /* Make a position announcement, if enabled */
04095    if (qe->parent->announcefrequency && qe->parent->announce_to_first_user) {
04096       say_position(qe, ringing);
04097    }
04098 
04099    /* Make a periodic announcement, if enabled */
04100    if (qe->parent->periodicannouncefrequency && qe->parent->announce_to_first_user) {
04101       say_periodic_announcement(qe, ringing);
04102    }
04103  
04104    if (!*to) {
04105       for (o = start; o; o = o->call_next) {
04106          rna(orig, qe, o->interface, o->member->membername, 1);
04107       }
04108    }
04109 
04110 #ifdef HAVE_EPOLL
04111    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
04112       if (epollo->chan)
04113          ast_poll_channel_del(in, epollo->chan);
04114    }
04115 #endif
04116 
04117    return peer;
04118 }

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

04238 {
04239    int res = 0;
04240 
04241    /* This is the holding pen for callers 2 through maxlen */
04242    for (;;) {
04243 
04244       if (is_our_turn(qe))
04245          break;
04246 
04247       /* If we have timed out, break out */
04248       if (qe->expire && (time(NULL) >= qe->expire)) {
04249          *reason = QUEUE_TIMEOUT;
04250          break;
04251       }
04252 
04253       if (qe->parent->leavewhenempty) {
04254          int status = 0;
04255 
04256          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
04257             *reason = QUEUE_LEAVEEMPTY;
04258             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04259             leave_queue(qe);
04260             break;
04261          }
04262       }
04263 
04264       /* Make a position announcement, if enabled */
04265       if (qe->parent->announcefrequency &&
04266          (res = say_position(qe,ringing)))
04267          break;
04268 
04269       /* If we have timed out, break out */
04270       if (qe->expire && (time(NULL) >= qe->expire)) {
04271          *reason = QUEUE_TIMEOUT;
04272          break;
04273       }
04274 
04275       /* Make a periodic announcement, if enabled */
04276       if (qe->parent->periodicannouncefrequency &&
04277          (res = say_periodic_announcement(qe,ringing)))
04278          break;
04279       
04280       /* see if we need to move to the next penalty level for this queue */
04281       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04282          update_qe_rule(qe);
04283       }
04284 
04285       /* If we have timed out, break out */
04286       if (qe->expire && (time(NULL) >= qe->expire)) {
04287          *reason = QUEUE_TIMEOUT;
04288          break;
04289       }
04290       
04291       /* Wait a second before checking again */
04292       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04293          if (res > 0 && !valid_exit(qe, res))
04294             res = 0;
04295          else
04296             break;
04297       }
04298       
04299       /* If we have timed out, break out */
04300       if (qe->expire && (time(NULL) >= qe->expire)) {
04301          *reason = QUEUE_TIMEOUT;
04302          break;
04303       }
04304    }
04305 
04306    return res;
04307 }

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

Referenced by complete_queue().

07399                                                             {
07400    int list_len, word_len = strlen(word);
07401    const char *find, *end_find, *end_list;
07402 
07403    /* strip whitespace from front */
07404    while (isspace(*list)) {
07405       list++;
07406    }
07407 
07408    while ((find = strstr(list, word))) {
07409       /* beginning of find starts inside another word? */
07410       if (find != list && *(find - 1) != ' ') {
07411          list = find;
07412          /* strip word from front */
07413          while (!isspace(*list) && *list != '\0') {
07414             list++;
07415          }
07416          /* strip whitespace from front */
07417          while (isspace(*list)) {
07418             list++;
07419          }
07420          continue;
07421       }
07422 
07423       /* end of find ends inside another word or at very end of list? */
07424       list_len = strlen(list);
07425       end_find = find + word_len;
07426       end_list = list + list_len;
07427       if (end_find == end_list || *end_find != ' ') {
07428          list = find;
07429          /* strip word from front */
07430          while (!isspace(*list) && *list != '\0') {
07431             list++;
07432          }
07433          /* strip whitespace from front */
07434          while (isspace(*list)) {
07435             list++;
07436          }
07437          continue;
07438       }
07439 
07440       /* terminating conditions satisfied, word at beginning or separated by ' ' */
07441       return 1;
07442    }
07443    
07444    return 0;
07445 }


Variable Documentation

char* app = "Queue" [static]

Definition at line 901 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 903 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 907 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 911 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 905 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 909 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 923 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

Definition at line 8443 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 932 of file app_queue.c.

Definition at line 885 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 926 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 914 of file app_queue.c.

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

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

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

Definition at line 8721 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

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

Definition at line 6756 of file app_queue.c.

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

Definition at line 6771 of file app_queue.c.

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

Definition at line 6766 of file app_queue.c.

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

Definition at line 6781 of file app_queue.c.

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

Definition at line 6786 of file app_queue.c.

struct ao2_container* queues [static]
Initial value:

Definition at line 8716 of file app_queue.c.

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

Definition at line 6761 of file app_queue.c.

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

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

int shared_lastcall = 1 [static]

queues.conf [general] option

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

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 920 of file app_queue.c.


Generated on 3 Apr 2014 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1