Tue Aug 20 16:34:43 2013

Asterisk developer's documentation


app_queue.c File Reference

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

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

Go to the source code of this file.

Data Structures

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

Defines

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

Enumerations

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

Functions

static char * __queues_show (struct mansession *s, int fd, int argc, const char *const *argv)
 Show queue(s) status and statistics.
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
 Add member to queue.
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, const char *data)
 AddQueueMember application.
 AST_DATA_STRUCTURE (queue_ent, DATA_EXPORT_QUEUE_ENT)
 AST_DATA_STRUCTURE (member, DATA_EXPORT_MEMBER)
 AST_DATA_STRUCTURE (call_queue, DATA_EXPORT_CALL_QUEUE)
static AST_LIST_HEAD_STATIC (rule_lists, rule_list)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"True Call Queueing",.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_DEVSTATE_CONSUMER,.nonoptreq="res_monitor",)
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
static int autopause2int (const char *autopause)
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
static void callattempt_free (struct callattempt *doomed)
static int can_ring_entry (struct queue_ent *qe, struct callattempt *call)
static void clear_queue (struct call_queue *q)
static int clear_stats (const char *queuename)
 Facilitates resetting statistics for a queue.
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset)
 Check if a given word is in a space-delimited list.
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_pause_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_rule_show (const char *line, const char *word, int pos, int state)
static char * complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static void copy_rules (struct queue_ent *qe, const char *rulename)
 Copy rule from global list into specified queue.
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (void *obj)
 Free queue's member list then its string fields.
static void device_state_cb (const struct ast_event *event, void *unused)
static void do_hang (struct callattempt *o)
 common hangup actions
static void do_print (struct mansession *s, int fd, const char *str)
 direct ouput to manager or cli with proper terminator
static void dump_queue_members (struct call_queue *pm_queue)
 Dump all members in a specific queue to the database.
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static int extension_state_cb (char *context, char *exten, enum ast_extension_states state, void *data)
static int extensionstate2devicestate (int state)
 Helper function which converts from extension state to device state values.
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
 Iterate through queue's member list and delete them.
static int get_member_penalty (char *queuename, char *interface)
static int get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
 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)
 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 3593 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 8424 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8488 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

Definition at line 8502 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 1327 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 1329 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 1330 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 4401 of file app_queue.c.

04401                            {
04402    CALLER,
04403    AGENT,
04404    TRANSFER
04405 };

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

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

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

05526 {
05527    struct call_queue *q;
05528    struct member *new_member, *old_member;
05529    int res = RES_NOSUCHQUEUE;
05530 
05531    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05532     * short-circuits if the queue is already in memory. */
05533    if (!(q = load_realtime_queue(queuename)))
05534       return res;
05535 
05536    ao2_lock(q);
05537    if ((old_member = interface_exists(q, interface)) == NULL) {
05538       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05539          new_member->dynamic = 1;
05540          member_add_to_queue(q, new_member);
05541          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05542             "Queue: %s\r\n"
05543             "Location: %s\r\n"
05544             "MemberName: %s\r\n"
05545             "Membership: %s\r\n"
05546             "Penalty: %d\r\n"
05547             "CallsTaken: %d\r\n"
05548             "LastCall: %d\r\n"
05549             "Status: %d\r\n"
05550             "Paused: %d\r\n",
05551             q->name, new_member->interface, new_member->membername,
05552             "dynamic",
05553             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05554             new_member->status, new_member->paused);
05555          
05556          ao2_ref(new_member, -1);
05557          new_member = NULL;
05558 
05559          if (dump)
05560             dump_queue_members(q);
05561          
05562          res = RES_OKAY;
05563       } else {
05564          res = RES_OUTOFMEMORY;
05565       }
05566    } else {
05567       ao2_ref(old_member, -1);
05568       res = RES_EXISTS;
05569    }
05570    ao2_unlock(q);
05571    queue_t_unref(q, "Expiring temporary reference");
05572 
05573    return res;
05574 }

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

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

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

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

AddQueueMember application.

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

05958 {
05959    int res=-1;
05960    char *parse, *temppos = NULL;
05961    AST_DECLARE_APP_ARGS(args,
05962       AST_APP_ARG(queuename);
05963       AST_APP_ARG(interface);
05964       AST_APP_ARG(penalty);
05965       AST_APP_ARG(options);
05966       AST_APP_ARG(membername);
05967       AST_APP_ARG(state_interface);
05968    );
05969    int penalty = 0;
05970 
05971    if (ast_strlen_zero(data)) {
05972       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05973       return -1;
05974    }
05975 
05976    parse = ast_strdupa(data);
05977 
05978    AST_STANDARD_APP_ARGS(args, parse);
05979 
05980    if (ast_strlen_zero(args.interface)) {
05981       args.interface = ast_strdupa(chan->name);
05982       temppos = strrchr(args.interface, '-');
05983       if (temppos)
05984          *temppos = '\0';
05985    }
05986 
05987    if (!ast_strlen_zero(args.penalty)) {
05988       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05989          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05990          penalty = 0;
05991       }
05992    }
05993 
05994    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05995    case RES_OKAY:
05996       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
05997       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05998       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05999       res = 0;
06000       break;
06001    case RES_EXISTS:
06002       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
06003       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
06004       res = 0;
06005       break;
06006    case RES_NOSUCHQUEUE:
06007       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
06008       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
06009       res = 0;
06010       break;
06011    case RES_OUTOFMEMORY:
06012       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
06013       break;
06014    }
06015 
06016    return res;
06017 }

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

References ast_channel_datastore_find().

Referenced by try_calling().

04506 {
04507    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04508 }

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

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

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

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

Calculate the metric of each member in the outgoing callattempts.

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

Return values:
-1 if penalties are exceeded
0 otherwise

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

04332 {
04333    /* disregarding penalty on too few members? */
04334    int membercount = ao2_container_count(q->members);
04335    unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04336 
04337    if (usepenalty) {
04338       if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
04339          (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
04340          return -1;
04341       }
04342    } else {
04343       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04344            membercount, q->penaltymemberslimit);
04345    }
04346 
04347    switch (q->strategy) {
04348    case QUEUE_STRATEGY_RINGALL:
04349       /* Everyone equal, except for penalty */
04350       tmp->metric = mem->penalty * 1000000 * usepenalty;
04351       break;
04352    case QUEUE_STRATEGY_LINEAR:
04353       if (pos < qe->linpos) {
04354          tmp->metric = 1000 + pos;
04355       } else {
04356          if (pos > qe->linpos)
04357             /* Indicate there is another priority */
04358             qe->linwrapped = 1;
04359          tmp->metric = pos;
04360       }
04361       tmp->metric += mem->penalty * 1000000 * usepenalty;
04362       break;
04363    case QUEUE_STRATEGY_RRORDERED:
04364    case QUEUE_STRATEGY_RRMEMORY:
04365       pos = mem->queuepos;
04366       if (pos < q->rrpos) {
04367          tmp->metric = 1000 + pos;
04368       } else {
04369          if (pos > q->rrpos)
04370             /* Indicate there is another priority */
04371             q->wrapped = 1;
04372          tmp->metric = pos;
04373       }
04374       tmp->metric += mem->penalty * 1000000 * usepenalty;
04375       break;
04376    case QUEUE_STRATEGY_RANDOM:
04377       tmp->metric = ast_random() % 1000;
04378       tmp->metric += mem->penalty * 1000000 * usepenalty;
04379       break;
04380    case QUEUE_STRATEGY_WRANDOM:
04381       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04382       break;
04383    case QUEUE_STRATEGY_FEWESTCALLS:
04384       tmp->metric = mem->calls;
04385       tmp->metric += mem->penalty * 1000000 * usepenalty;
04386       break;
04387    case QUEUE_STRATEGY_LEASTRECENT:
04388       if (!mem->lastcall)
04389          tmp->metric = 0;
04390       else
04391          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04392       tmp->metric += mem->penalty * 1000000 * usepenalty;
04393       break;
04394    default:
04395       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04396       break;
04397    }
04398    return 0;
04399 }

static void callattempt_free ( struct callattempt doomed  )  [static]

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

02924 {
02925    if (doomed->member) {
02926       ao2_ref(doomed->member, -1);
02927    }
02928    ast_party_connected_line_free(&doomed->connected);
02929    ast_free(doomed);
02930 }

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

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

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

static void clear_queue ( struct call_queue q  )  [static]

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

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

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

07164 {
07165    struct call_queue *q;
07166    struct ao2_iterator queue_iter;
07167 
07168    queue_iter = ao2_iterator_init(queues, 0);
07169    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07170       ao2_lock(q);
07171       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
07172          clear_queue(q);
07173       ao2_unlock(q);
07174       queue_t_unref(q, "Done with iterator");
07175    }
07176    ao2_iterator_destroy(&queue_iter);
07177    return 0;
07178 }

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

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

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

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

07428 {
07429    struct call_queue *q;
07430    char *ret = NULL;
07431    int which = 0;
07432    int wordlen = strlen(word);
07433    struct ao2_iterator queue_iter;
07434    const char *word_list = NULL;
07435 
07436    /* for certain commands, already completed items should be left out of
07437     * the list */
07438    if (word_list_offset && strlen(line) >= word_list_offset) {
07439       word_list = line + word_list_offset;
07440    }
07441 
07442    queue_iter = ao2_iterator_init(queues, 0);
07443    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07444       if (!strncasecmp(word, q->name, wordlen) && ++which > state
07445          && (!word_list_offset || !word_in_list(word_list, q->name))) {
07446          ret = ast_strdup(q->name);
07447          queue_t_unref(q, "Done with iterator");
07448          break;
07449       }
07450       queue_t_unref(q, "Done with iterator");
07451    }
07452    ao2_iterator_destroy(&queue_iter);
07453 
07454    /* Pretend "rules" is at the end of the queues list in certain
07455     * circumstances since it is an alternate command that should be
07456     * tab-completable for "queue show" */
07457    if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
07458       ret = ast_strdup("rules");
07459    }
07460 
07461    return ret;
07462 }

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

Definition at line 7889 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07890 {
07891    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07892    switch (pos) {
07893    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07894       return NULL;
07895    case 4: /* only one possible match, "to" */
07896       return state == 0 ? ast_strdup("to") : NULL;
07897    case 5: /* <queue> */
07898       return complete_queue(line, word, pos, state, 0);
07899    case 6: /* only one possible match, "penalty" */
07900       return state == 0 ? ast_strdup("penalty") : NULL;
07901    case 7:
07902       if (state < 100) {      /* 0-99 */
07903          char *num;
07904          if ((num = ast_malloc(3))) {
07905             sprintf(num, "%d", state);
07906          }
07907          return num;
07908       } else {
07909          return NULL;
07910       }
07911    case 8: /* only one possible match, "as" */
07912       return state == 0 ? ast_strdup("as") : NULL;
07913    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
07914       return NULL;
07915    default:
07916       return NULL;
07917    }
07918 }

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

Definition at line 8111 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

08112 {
08113    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
08114    switch (pos) {
08115    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
08116       return NULL;
08117    case 4:  /* only one possible match, "queue" */
08118       return state == 0 ? ast_strdup("queue") : NULL;
08119    case 5:  /* <queue> */
08120       return complete_queue(line, word, pos, state, 0);
08121    case 6: /* "reason" */
08122       return state == 0 ? ast_strdup("reason") : NULL;
08123    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
08124       return NULL;
08125    default:
08126       return NULL;
08127    }
08128 }

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

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

08020 {
08021    int which = 0;
08022    struct call_queue *q;
08023    struct member *m;
08024    struct ao2_iterator queue_iter;
08025    struct ao2_iterator mem_iter;
08026    int wordlen = strlen(word);
08027 
08028    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
08029    if (pos > 5 || pos < 3)
08030       return NULL;
08031    if (pos == 4)   /* only one possible match, 'from' */
08032       return (state == 0 ? ast_strdup("from") : NULL);
08033 
08034    if (pos == 5) {  /* No need to duplicate code */
08035       return complete_queue(line, word, pos, state, 0);
08036    }
08037 
08038    /* here is the case for 3, <member> */
08039    queue_iter = ao2_iterator_init(queues, 0);
08040    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
08041       ao2_lock(q);
08042       mem_iter = ao2_iterator_init(q->members, 0);
08043       while ((m = ao2_iterator_next(&mem_iter))) {
08044          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
08045             char *tmp;
08046             tmp = ast_strdup(m->interface);
08047             ao2_ref(m, -1);
08048             ao2_iterator_destroy(&mem_iter);
08049             ao2_unlock(q);
08050             queue_t_unref(q, "Done with iterator, returning interface name");
08051             ao2_iterator_destroy(&queue_iter);
08052             return tmp;
08053          }
08054          ao2_ref(m, -1);
08055       }
08056       ao2_iterator_destroy(&mem_iter);
08057       ao2_unlock(q);
08058       queue_t_unref(q, "Done with iterator");
08059    }
08060    ao2_iterator_destroy(&queue_iter);
08061 
08062    return NULL;
08063 }

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

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

08245 {
08246    int which = 0;
08247    struct rule_list *rl_iter;
08248    int wordlen = strlen(word);
08249    char *ret = NULL;
08250    if (pos != 3) /* Wha? */ {
08251       return NULL;
08252    }
08253 
08254    AST_LIST_LOCK(&rule_lists);
08255    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08256       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
08257          ret = ast_strdup(rl_iter->name);
08258          break;
08259       }
08260    }
08261    AST_LIST_UNLOCK(&rule_lists);
08262 
08263    return ret;
08264 }

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

Definition at line 8181 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

08182 {
08183    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
08184    switch (pos) {
08185    case 4:
08186       if (state == 0) {
08187          return ast_strdup("on");
08188       } else {
08189          return NULL;
08190       }
08191    case 6:
08192       if (state == 0) {
08193          return ast_strdup("in");
08194       } else {
08195          return NULL;
08196       }
08197    case 7:
08198       return complete_queue(line, word, pos, state, 0);
08199    default:
08200       return NULL;
08201    }
08202 }

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

Definition at line 7464 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07465 {
07466    if (pos == 2) {
07467       return complete_queue(line, word, pos, state, 0);
07468    }
07469    return NULL;
07470 }

static int compress_char ( const char  c  )  [static]

Definition at line 1684 of file app_queue.c.

Referenced by member_hash_fn().

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

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

Copy rule from global list into specified queue.

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

06055 {
06056    struct penalty_rule *pr_iter;
06057    struct rule_list *rl_iter;
06058    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
06059    AST_LIST_LOCK(&rule_lists);
06060    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06061       if (!strcasecmp(rl_iter->name, tmp))
06062          break;
06063    }
06064    if (rl_iter) {
06065       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06066          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
06067          if (!new_pr) {
06068             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
06069             break;
06070          }
06071          new_pr->time = pr_iter->time;
06072          new_pr->max_value = pr_iter->max_value;
06073          new_pr->min_value = pr_iter->min_value;
06074          new_pr->max_relative = pr_iter->max_relative;
06075          new_pr->min_relative = pr_iter->min_relative;
06076          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
06077       }
06078    }
06079    AST_LIST_UNLOCK(&rule_lists);
06080 }

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

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

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

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

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

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

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

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 3038 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

03039 {
03040    o->stillgoing = 0;
03041    ast_hangup(o->chan);
03042    o->chan = NULL;
03043 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

07211 {
07212    if (s)
07213       astman_append(s, "%s\r\n", str);
07214    else
07215       ast_cli(fd, "%s\n", str);
07216 }

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

05426 {
05427    struct member *cur_member;
05428    struct ast_str *value;
05429    struct ao2_iterator mem_iter;
05430 
05431    if (!pm_queue) {
05432       return;
05433    }
05434 
05435    /* 4K is a reasonable default for most applications, but we grow to
05436     * accommodate more if necessary. */
05437    if (!(value = ast_str_create(4096))) {
05438       return;
05439    }
05440 
05441    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05442    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05443       if (!cur_member->dynamic) {
05444          ao2_ref(cur_member, -1);
05445          continue;
05446       }
05447 
05448       ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s",
05449          ast_str_strlen(value) ? "|" : "",
05450          cur_member->interface,
05451          cur_member->penalty,
05452          cur_member->paused,
05453          cur_member->membername,
05454          cur_member->state_interface);
05455 
05456       ao2_ref(cur_member, -1);
05457    }
05458    ao2_iterator_destroy(&mem_iter);
05459 
05460    if (ast_str_strlen(value) && !cur_member) {
05461       if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value)))
05462          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05463    } else {
05464       /* Delete the entry if the queue is empty or there is an error */
05465       ast_db_del(pm_family, pm_queue->name);
05466    }
05467 
05468    ast_free(value);
05469 }

static void end_bridge_callback ( void *  data  )  [static]

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

04554 {
04555    struct queue_end_bridge *qeb = data;
04556    struct call_queue *q = qeb->q;
04557    struct ast_channel *chan = qeb->chan;
04558 
04559    if (ao2_ref(qeb, -1) == 1) {
04560       set_queue_variables(q, chan);
04561       /* This unrefs the reference we made in try_calling when we allocated qeb */
04562       queue_t_unref(q, "Expire bridge_config reference");
04563    }
04564 }

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

Definition at line 4546 of file app_queue.c.

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

Referenced by try_calling().

04547 {
04548    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04549    ao2_ref(qeb, +1);
04550    qeb->chan = originator;
04551 }

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

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

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

static int extensionstate2devicestate ( int  state  )  [static]

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

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

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

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

find the entry with the best metric, or NULL

Definition at line 3358 of file app_queue.c.

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

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

03359 {
03360    struct callattempt *best = NULL, *cur;
03361 
03362    for (cur = outgoing; cur; cur = cur->q_next) {
03363       if (cur->stillgoing &&              /* Not already done */
03364          !cur->chan &&              /* Isn't already going */
03365          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03366          best = cur;
03367       }
03368    }
03369 
03370    return best;
03371 }

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

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

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

Iterate through queue's member list and delete them.

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

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

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

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

05703 {
05704    int foundqueue = 0, penalty;
05705    struct call_queue *q, tmpq = {
05706       .name = queuename,   
05707    };
05708    struct member *mem;
05709    
05710    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05711       foundqueue = 1;
05712       ao2_lock(q);
05713       if ((mem = interface_exists(q, interface))) {
05714          penalty = mem->penalty;
05715          ao2_ref(mem, -1);
05716          ao2_unlock(q);
05717          queue_t_unref(q, "Search complete");
05718          return penalty;
05719       }
05720       ao2_unlock(q);
05721       queue_t_unref(q, "Search complete");
05722    }
05723 
05724    /* some useful debuging */
05725    if (foundqueue) 
05726       ast_log (LOG_ERROR, "Invalid queuename\n");
05727    else 
05728       ast_log (LOG_ERROR, "Invalid interface\n");
05729 
05730    return RESULT_FAILURE;
05731 }

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

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

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

07946 {
07947    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07948    int penalty;
07949 
07950    switch ( cmd ) {
07951    case CLI_INIT:
07952       e->command = "queue add member";
07953       e->usage =
07954          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07955          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
07956       return NULL;
07957    case CLI_GENERATE:
07958       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07959    }
07960 
07961    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07962       return CLI_SHOWUSAGE;
07963    } else if (strcmp(a->argv[4], "to")) {
07964       return CLI_SHOWUSAGE;
07965    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07966       return CLI_SHOWUSAGE;
07967    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07968       return CLI_SHOWUSAGE;
07969    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07970       return CLI_SHOWUSAGE;
07971    }
07972 
07973    queuename = a->argv[5];
07974    interface = a->argv[3];
07975    if (a->argc >= 8) {
07976       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07977          if (penalty < 0) {
07978             ast_cli(a->fd, "Penalty must be >= 0\n");
07979             penalty = 0;
07980          }
07981       } else {
07982          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07983          penalty = 0;
07984       }
07985    } else {
07986       penalty = 0;
07987    }
07988 
07989    if (a->argc >= 10) {
07990       membername = a->argv[9];
07991    }
07992 
07993    if (a->argc >= 12) {
07994       state_interface = a->argv[11];
07995    }
07996 
07997    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07998    case RES_OKAY:
07999       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
08000       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
08001       return CLI_SUCCESS;
08002    case RES_EXISTS:
08003       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
08004       return CLI_FAILURE;
08005    case RES_NOSUCHQUEUE:
08006       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
08007       return CLI_FAILURE;
08008    case RES_OUTOFMEMORY:
08009       ast_cli(a->fd, "Out of memory\n");
08010       return CLI_FAILURE;
08011    case RES_NOT_DYNAMIC:
08012       ast_cli(a->fd, "Member not dynamic\n");
08013       return CLI_FAILURE;
08014    default:
08015       return CLI_FAILURE;
08016    }
08017 }

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

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

08131 {
08132    const char *queuename, *interface, *reason;
08133    int paused;
08134 
08135    switch (cmd) {
08136    case CLI_INIT:
08137       e->command = "queue {pause|unpause} member";
08138       e->usage = 
08139          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
08140          "  Pause or unpause a queue member. Not specifying a particular queue\n"
08141          "  will pause or unpause a member across all queues to which the member\n"
08142          "  belongs.\n";
08143       return NULL;
08144    case CLI_GENERATE:
08145       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
08146    }
08147 
08148    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
08149       return CLI_SHOWUSAGE;
08150    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
08151       return CLI_SHOWUSAGE;
08152    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
08153       return CLI_SHOWUSAGE;
08154    }
08155 
08156 
08157    interface = a->argv[3];
08158    queuename = a->argc >= 6 ? a->argv[5] : NULL;
08159    reason = a->argc == 8 ? a->argv[7] : NULL;
08160    paused = !strcasecmp(a->argv[1], "pause");
08161 
08162    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
08163       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
08164       if (!ast_strlen_zero(queuename))
08165          ast_cli(a->fd, " in queue '%s'", queuename);
08166       if (!ast_strlen_zero(reason))
08167          ast_cli(a->fd, " for reason '%s'", reason);
08168       ast_cli(a->fd, "\n");
08169       return CLI_SUCCESS;
08170    } else {
08171       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
08172       if (!ast_strlen_zero(queuename))
08173          ast_cli(a->fd, " in queue '%s'", queuename);
08174       if (!ast_strlen_zero(reason))
08175          ast_cli(a->fd, " for reason '%s'", reason);
08176       ast_cli(a->fd, "\n");
08177       return CLI_FAILURE;
08178    }
08179 }

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

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

08340 {
08341    struct ast_flags mask = {0,};
08342    int i;
08343 
08344    switch (cmd) {
08345       case CLI_INIT:
08346          e->command = "queue reload {parameters|members|rules|all}";
08347          e->usage =
08348             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08349             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08350             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08351             "specified in order to know what information to reload. Below is an explanation\n"
08352             "of each of these qualifiers.\n"
08353             "\n"
08354             "\t'members' - reload queue members from queues.conf\n"
08355             "\t'parameters' - reload all queue options except for queue members\n"
08356             "\t'rules' - reload the queuerules.conf file\n"
08357             "\t'all' - reload queue rules, parameters, and members\n"
08358             "\n"
08359             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08360             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08361             "one queue is specified when using this command, reloading queue rules may cause\n"
08362             "other queues to be affected\n";
08363          return NULL;
08364       case CLI_GENERATE:
08365          if (a->pos >= 3) {
08366             /* find the point at which the list of queue names starts */
08367             const char *command_end = a->line + strlen("queue reload ");
08368             command_end = strchr(command_end, ' ');
08369             if (!command_end) {
08370                command_end = a->line + strlen(a->line);
08371             }
08372             return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
08373          } else {
08374             return NULL;
08375          }
08376    }
08377 
08378    if (a->argc < 3)
08379       return CLI_SHOWUSAGE;
08380 
08381    if (!strcasecmp(a->argv[2], "rules")) {
08382       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08383    } else if (!strcasecmp(a->argv[2], "members")) {
08384       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08385    } else if (!strcasecmp(a->argv[2], "parameters")) {
08386       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08387    } else if (!strcasecmp(a->argv[2], "all")) {
08388       ast_set_flag(&mask, AST_FLAGS_ALL);
08389    }
08390 
08391    if (a->argc == 3) {
08392       reload_handler(1, &mask, NULL);
08393       return CLI_SUCCESS;
08394    }
08395 
08396    for (i = 3; i < a->argc; ++i) {
08397       reload_handler(1, &mask, a->argv[i]);
08398    }
08399 
08400    return CLI_SUCCESS;
08401 }

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

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

08066 {
08067    const char *queuename, *interface;
08068 
08069    switch (cmd) {
08070    case CLI_INIT:
08071       e->command = "queue remove member";
08072       e->usage = 
08073          "Usage: queue remove member <channel> from <queue>\n"
08074          "       Remove a specific channel from a queue.\n";
08075       return NULL;
08076    case CLI_GENERATE:
08077       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
08078    }
08079 
08080    if (a->argc != 6) {
08081       return CLI_SHOWUSAGE;
08082    } else if (strcmp(a->argv[4], "from")) {
08083       return CLI_SHOWUSAGE;
08084    }
08085 
08086    queuename = a->argv[5];
08087    interface = a->argv[3];
08088 
08089    switch (remove_from_queue(queuename, interface)) {
08090    case RES_OKAY:
08091       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
08092       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
08093       return CLI_SUCCESS;
08094    case RES_EXISTS:
08095       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
08096       return CLI_FAILURE;
08097    case RES_NOSUCHQUEUE:
08098       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
08099       return CLI_FAILURE;
08100    case RES_OUTOFMEMORY:
08101       ast_cli(a->fd, "Out of memory\n");
08102       return CLI_FAILURE;
08103    case RES_NOT_DYNAMIC:
08104       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
08105       return CLI_FAILURE;
08106    default:
08107       return CLI_FAILURE;
08108    }
08109 }

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

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

08301 {
08302    struct ast_flags mask = {QUEUE_RESET_STATS,};
08303    int i;
08304 
08305    switch (cmd) {
08306       case CLI_INIT:
08307          e->command = "queue reset stats";
08308          e->usage =
08309             "Usage: queue reset stats [<queuenames>]\n"
08310             "\n"
08311             "Issuing this command will reset statistics for\n"
08312             "<queuenames>, or for all queues if no queue is\n"
08313             "specified.\n";
08314          return NULL;
08315       case CLI_GENERATE:
08316          if (a->pos >= 3) {
08317             return complete_queue(a->line, a->word, a->pos, a->n, 17);
08318          } else {
08319             return NULL;
08320          }
08321    }
08322 
08323    if (a->argc < 3) {
08324       return CLI_SHOWUSAGE;
08325    }
08326 
08327    if (a->argc == 3) {
08328       reload_handler(1, &mask, NULL);
08329       return CLI_SUCCESS;
08330    }
08331 
08332    for (i = 3; i < a->argc; ++i) {
08333       reload_handler(1, &mask, a->argv[i]);
08334    }
08335 
08336    return CLI_SUCCESS;
08337 }

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

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

08267 {
08268    const char *rule;
08269    struct rule_list *rl_iter;
08270    struct penalty_rule *pr_iter;
08271    switch (cmd) {
08272    case CLI_INIT:
08273       e->command = "queue show rules";
08274       e->usage =
08275       "Usage: queue show rules [rulename]\n"
08276       "  Show the list of rules associated with rulename. If no\n"
08277       "  rulename is specified, list all rules defined in queuerules.conf\n";
08278       return NULL;
08279    case CLI_GENERATE:
08280       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
08281    }
08282 
08283    if (a->argc != 3 && a->argc != 4)
08284       return CLI_SHOWUSAGE;
08285 
08286    rule = a->argc == 4 ? a->argv[3] : "";
08287    AST_LIST_LOCK(&rule_lists);
08288    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08289       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
08290          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
08291          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
08292             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);
08293          }
08294       }
08295    }
08296    AST_LIST_UNLOCK(&rule_lists);
08297    return CLI_SUCCESS; 
08298 }

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

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

08205 {
08206    const char *queuename = NULL, *interface;
08207    int penalty = 0;
08208 
08209    switch (cmd) {
08210    case CLI_INIT:
08211       e->command = "queue set penalty";
08212       e->usage = 
08213       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
08214       "  Set a member's penalty in the queue specified. If no queue is specified\n"
08215       "  then that interface's penalty is set in all queues to which that interface is a member\n";
08216       return NULL;
08217    case CLI_GENERATE:
08218       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
08219    }
08220 
08221    if (a->argc != 6 && a->argc != 8) {
08222       return CLI_SHOWUSAGE;
08223    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
08224       return CLI_SHOWUSAGE;
08225    }
08226 
08227    if (a->argc == 8)
08228       queuename = a->argv[7];
08229    interface = a->argv[5];
08230    penalty = atoi(a->argv[3]);
08231 
08232    switch (set_member_penalty(queuename, interface, penalty)) {
08233    case RESULT_SUCCESS:
08234       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08235       return CLI_SUCCESS;
08236    case RESULT_FAILURE:
08237       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08238       return CLI_FAILURE;
08239    default:
08240       return CLI_FAILURE;
08241    }
08242 }

static int handle_statechange ( void *  datap  )  [static]

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

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

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

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

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

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

References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::autopause, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, 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().

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

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

References call_queue::head, and queue_ref().

Referenced by join_queue().

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

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

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

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

Definition at line 1208 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

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

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

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

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

05400 {
05401    struct member *mem;
05402    struct ao2_iterator mem_iter;
05403 
05404    if (!q)
05405       return NULL;
05406 
05407    mem_iter = ao2_iterator_init(q->members, 0);
05408    while ((mem = ao2_iterator_next(&mem_iter))) {
05409       if (!strcasecmp(interface, mem->interface)) {
05410          ao2_iterator_destroy(&mem_iter);
05411          return mem;
05412       }
05413       ao2_ref(mem, -1);
05414    }
05415    ao2_iterator_destroy(&mem_iter);
05416 
05417    return NULL;
05418 }

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

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

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

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

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

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

Definition at line 6931 of file app_queue.c.

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

Referenced by reload_single_queue().

06932 {
06933    struct member *member = obj;
06934 
06935    if (!member->delme) {
06936       member->status = get_queue_member_status(member);
06937       return 0;
06938    } else {
06939       return CMP_MATCH;
06940    }
06941 }

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

Definition at line 7075 of file app_queue.c.

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

Referenced by reload_queues().

07076 {
07077    struct call_queue *q = obj;
07078    char *queuename = arg;
07079    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
07080       return CMP_MATCH;
07081    } else {
07082       return 0;
07083    }
07084 }

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

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

static int load_module ( void   )  [static]

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

08749 {
08750    int res;
08751    struct ast_context *con;
08752    struct ast_flags mask = {AST_FLAGS_ALL, };
08753 
08754    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08755 
08756    use_weight = 0;
08757 
08758    if (reload_handler(0, &mask, NULL))
08759       return AST_MODULE_LOAD_DECLINE;
08760 
08761    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08762    if (!con)
08763       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08764    else
08765       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08766 
08767    if (queue_persistent_members)
08768       reload_queue_members();
08769 
08770    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08771 
08772    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08773    res = ast_register_application_xml(app, queue_exec);
08774    res |= ast_register_application_xml(app_aqm, aqm_exec);
08775    res |= ast_register_application_xml(app_rqm, rqm_exec);
08776    res |= ast_register_application_xml(app_pqm, pqm_exec);
08777    res |= ast_register_application_xml(app_upqm, upqm_exec);
08778    res |= ast_register_application_xml(app_ql, ql_exec);
08779    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08780    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08781    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08782    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08783    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08784    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08785    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08786    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08787    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08788    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08789    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08790    res |= ast_custom_function_register(&queuevar_function);
08791    res |= ast_custom_function_register(&queueexists_function);
08792    res |= ast_custom_function_register(&queuemembercount_function);
08793    res |= ast_custom_function_register(&queuemembercount_dep);
08794    res |= ast_custom_function_register(&queuememberlist_function);
08795    res |= ast_custom_function_register(&queuewaitingcount_function);
08796    res |= ast_custom_function_register(&queuememberpenalty_function);
08797 
08798    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08799       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08800    }
08801 
08802    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08803    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08804       res = -1;
08805    }
08806 
08807    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08808 
08809    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08810 
08811    return res ? AST_MODULE_LOAD_DECLINE : 0;
08812 }

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

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

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

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

07713 {
07714    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07715    int paused, penalty = 0;
07716 
07717    queuename = astman_get_header(m, "Queue");
07718    interface = astman_get_header(m, "Interface");
07719    penalty_s = astman_get_header(m, "Penalty");
07720    paused_s = astman_get_header(m, "Paused");
07721    membername = astman_get_header(m, "MemberName");
07722    state_interface = astman_get_header(m, "StateInterface");
07723 
07724    if (ast_strlen_zero(queuename)) {
07725       astman_send_error(s, m, "'Queue' not specified.");
07726       return 0;
07727    }
07728 
07729    if (ast_strlen_zero(interface)) {
07730       astman_send_error(s, m, "'Interface' not specified.");
07731       return 0;
07732    }
07733 
07734    if (ast_strlen_zero(penalty_s))
07735       penalty = 0;
07736    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07737       penalty = 0;
07738 
07739    if (ast_strlen_zero(paused_s))
07740       paused = 0;
07741    else
07742       paused = abs(ast_true(paused_s));
07743 
07744    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07745    case RES_OKAY:
07746       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07747       astman_send_ack(s, m, "Added interface to queue");
07748       break;
07749    case RES_EXISTS:
07750       astman_send_error(s, m, "Unable to add interface: Already there");
07751       break;
07752    case RES_NOSUCHQUEUE:
07753       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07754       break;
07755    case RES_OUTOFMEMORY:
07756       astman_send_error(s, m, "Out of memory");
07757       break;
07758    }
07759 
07760    return 0;
07761 }

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

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

07798 {
07799    const char *queuename, *interface, *paused_s, *reason;
07800    int paused;
07801 
07802    interface = astman_get_header(m, "Interface");
07803    paused_s = astman_get_header(m, "Paused");
07804    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07805    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07806 
07807    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07808       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07809       return 0;
07810    }
07811 
07812    paused = abs(ast_true(paused_s));
07813 
07814    if (set_member_paused(queuename, interface, reason, paused))
07815       astman_send_error(s, m, "Interface not found");
07816    else
07817       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07818    return 0;
07819 }

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

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

07822 {
07823    const char *queuename, *event, *message, *interface, *uniqueid;
07824 
07825    queuename = astman_get_header(m, "Queue");
07826    uniqueid = astman_get_header(m, "UniqueId");
07827    interface = astman_get_header(m, "Interface");
07828    event = astman_get_header(m, "Event");
07829    message = astman_get_header(m, "Message");
07830 
07831    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07832       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07833       return 0;
07834    }
07835 
07836    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07837    astman_send_ack(s, m, "Event added successfully");
07838 
07839    return 0;
07840 }

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

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

07921 {
07922    const char *queuename, *interface, *penalty_s;
07923    int penalty;
07924 
07925    interface = astman_get_header(m, "Interface");
07926    penalty_s = astman_get_header(m, "Penalty");
07927    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
07928    queuename = astman_get_header(m, "Queue");
07929 
07930    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07931       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07932       return 0;
07933    }
07934  
07935    penalty = atoi(penalty_s);
07936 
07937    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07938       astman_send_error(s, m, "Invalid interface, queuename or penalty");
07939    else
07940       astman_send_ack(s, m, "Interface penalty set successfully");
07941 
07942    return 0;
07943 }

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

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

07843 {
07844    struct ast_flags mask = {0,};
07845    const char *queuename = NULL;
07846    int header_found = 0;
07847 
07848    queuename = astman_get_header(m, "Queue");
07849    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07850       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07851       header_found = 1;
07852    }
07853    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07854       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07855       header_found = 1;
07856    }
07857    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07858       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07859       header_found = 1;
07860    }
07861 
07862    if (!header_found) {
07863       ast_set_flag(&mask, AST_FLAGS_ALL);
07864    }
07865 
07866    if (!reload_handler(1, &mask, queuename)) {
07867       astman_send_ack(s, m, "Queue reloaded successfully");
07868    } else {
07869       astman_send_error(s, m, "Error encountered while reloading queue");
07870    }
07871    return 0;
07872 }

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

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

07875 {
07876    const char *queuename = NULL;
07877    struct ast_flags mask = {QUEUE_RESET_STATS,};
07878    
07879    queuename = astman_get_header(m, "Queue");
07880 
07881    if (!reload_handler(1, &mask, queuename)) {
07882       astman_send_ack(s, m, "Queue stats reset successfully");
07883    } else {
07884       astman_send_error(s, m, "Error encountered while resetting queue stats");
07885    }
07886    return 0;
07887 }

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

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

07502 {
07503    const char *rule = astman_get_header(m, "Rule");
07504    const char *id = astman_get_header(m, "ActionID");
07505    struct rule_list *rl_iter;
07506    struct penalty_rule *pr_iter;
07507 
07508    astman_append(s, "Response: Success\r\n");
07509    if (!ast_strlen_zero(id)) {
07510       astman_append(s, "ActionID: %s\r\n", id);
07511    }
07512 
07513    AST_LIST_LOCK(&rule_lists);
07514    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07515       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07516          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07517          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07518             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 );
07519          }
07520          if (!ast_strlen_zero(rule))
07521             break;
07522       }
07523    }
07524    AST_LIST_UNLOCK(&rule_lists);
07525 
07526    /*
07527     * Two blank lines instead of one because the Response and
07528     * ActionID headers used to not be present.
07529     */
07530    astman_append(s, "\r\n\r\n");
07531 
07532    return RESULT_SUCCESS;
07533 }

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

Definition at line 7491 of file app_queue.c.

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

Referenced by load_module().

07492 {
07493    static const char * const a[] = { "queue", "show" };
07494 
07495    __queues_show(s, -1, 2, a);
07496    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07497 
07498    return RESULT_SUCCESS;
07499 }

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

Queue status info via AMI.

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

07612 {
07613    time_t now;
07614    int pos;
07615    const char *id = astman_get_header(m,"ActionID");
07616    const char *queuefilter = astman_get_header(m,"Queue");
07617    const char *memberfilter = astman_get_header(m,"Member");
07618    char idText[256] = "";
07619    struct call_queue *q;
07620    struct queue_ent *qe;
07621    float sl = 0;
07622    struct member *mem;
07623    struct ao2_iterator queue_iter;
07624    struct ao2_iterator mem_iter;
07625 
07626    astman_send_ack(s, m, "Queue status will follow");
07627    time(&now);
07628    if (!ast_strlen_zero(id))
07629       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07630 
07631    queue_iter = ao2_iterator_init(queues, 0);
07632    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07633       ao2_lock(q);
07634 
07635       /* List queue properties */
07636       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07637          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07638          astman_append(s, "Event: QueueParams\r\n"
07639             "Queue: %s\r\n"
07640             "Max: %d\r\n"
07641             "Strategy: %s\r\n"
07642             "Calls: %d\r\n"
07643             "Holdtime: %d\r\n"
07644             "TalkTime: %d\r\n"
07645             "Completed: %d\r\n"
07646             "Abandoned: %d\r\n"
07647             "ServiceLevel: %d\r\n"
07648             "ServicelevelPerf: %2.1f\r\n"
07649             "Weight: %d\r\n"
07650             "%s"
07651             "\r\n",
07652             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07653             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07654          /* List Queue Members */
07655          mem_iter = ao2_iterator_init(q->members, 0);
07656          while ((mem = ao2_iterator_next(&mem_iter))) {
07657             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07658                astman_append(s, "Event: QueueMember\r\n"
07659                   "Queue: %s\r\n"
07660                   "Name: %s\r\n"
07661                   "Location: %s\r\n"
07662                   "Membership: %s\r\n"
07663                   "Penalty: %d\r\n"
07664                   "CallsTaken: %d\r\n"
07665                   "LastCall: %d\r\n"
07666                   "Status: %d\r\n"
07667                   "Paused: %d\r\n"
07668                   "%s"
07669                   "\r\n",
07670                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07671                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07672             }
07673             ao2_ref(mem, -1);
07674          }
07675          ao2_iterator_destroy(&mem_iter);
07676          /* List Queue Entries */
07677          pos = 1;
07678          for (qe = q->head; qe; qe = qe->next) {
07679             astman_append(s, "Event: QueueEntry\r\n"
07680                "Queue: %s\r\n"
07681                "Position: %d\r\n"
07682                "Channel: %s\r\n"
07683                "Uniqueid: %s\r\n"
07684                "CallerIDNum: %s\r\n"
07685                "CallerIDName: %s\r\n"
07686                "ConnectedLineNum: %s\r\n"
07687                "ConnectedLineName: %s\r\n"
07688                "Wait: %ld\r\n"
07689                "%s"
07690                "\r\n",
07691                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07692                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07693                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07694                S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07695                S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07696                (long) (now - qe->start), idText);
07697          }
07698       }
07699       ao2_unlock(q);
07700       queue_t_unref(q, "Done with iterator");
07701    }
07702    ao2_iterator_destroy(&queue_iter);
07703 
07704    astman_append(s,
07705       "Event: QueueStatusComplete\r\n"
07706       "%s"
07707       "\r\n",idText);
07708 
07709    return RESULT_SUCCESS;
07710 }

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

Summary of queue info via the AMI.

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

07537 {
07538    time_t now;
07539    int qmemcount = 0;
07540    int qmemavail = 0;
07541    int qchancount = 0;
07542    int qlongestholdtime = 0;
07543    const char *id = astman_get_header(m, "ActionID");
07544    const char *queuefilter = astman_get_header(m, "Queue");
07545    char idText[256] = "";
07546    struct call_queue *q;
07547    struct queue_ent *qe;
07548    struct member *mem;
07549    struct ao2_iterator queue_iter;
07550    struct ao2_iterator mem_iter;
07551 
07552    astman_send_ack(s, m, "Queue summary will follow");
07553    time(&now);
07554    if (!ast_strlen_zero(id))
07555       snprintf(idText, 256, "ActionID: %s\r\n", id);
07556    queue_iter = ao2_iterator_init(queues, 0);
07557    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07558       ao2_lock(q);
07559 
07560       /* List queue properties */
07561       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07562          /* Reset the necessary local variables if no queuefilter is set*/
07563          qmemcount = 0;
07564          qmemavail = 0;
07565          qchancount = 0;
07566          qlongestholdtime = 0;
07567 
07568          /* List Queue Members */
07569          mem_iter = ao2_iterator_init(q->members, 0);
07570          while ((mem = ao2_iterator_next(&mem_iter))) {
07571             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07572                ++qmemcount;
07573                if (member_status_available(mem->status) && !mem->paused) {
07574                   ++qmemavail;
07575                }
07576             }
07577             ao2_ref(mem, -1);
07578          }
07579          ao2_iterator_destroy(&mem_iter);
07580          for (qe = q->head; qe; qe = qe->next) {
07581             if ((now - qe->start) > qlongestholdtime) {
07582                qlongestholdtime = now - qe->start;
07583             }
07584             ++qchancount;
07585          }
07586          astman_append(s, "Event: QueueSummary\r\n"
07587             "Queue: %s\r\n"
07588             "LoggedIn: %d\r\n"
07589             "Available: %d\r\n"
07590             "Callers: %d\r\n" 
07591             "HoldTime: %d\r\n"
07592             "TalkTime: %d\r\n"
07593             "LongestHoldTime: %d\r\n"
07594             "%s"
07595             "\r\n",
07596             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07597       }
07598       ao2_unlock(q);
07599       queue_t_unref(q, "Done with iterator");
07600    }
07601    ao2_iterator_destroy(&queue_iter);
07602    astman_append(s,
07603       "Event: QueueSummaryComplete\r\n"
07604       "%s"
07605       "\r\n", idText);
07606 
07607    return RESULT_SUCCESS;
07608 }

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

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

07764 {
07765    const char *queuename, *interface;
07766 
07767    queuename = astman_get_header(m, "Queue");
07768    interface = astman_get_header(m, "Interface");
07769 
07770    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07771       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07772       return 0;
07773    }
07774 
07775    switch (remove_from_queue(queuename, interface)) {
07776    case RES_OKAY:
07777       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07778       astman_send_ack(s, m, "Removed interface from queue");
07779       break;
07780    case RES_EXISTS:
07781       astman_send_error(s, m, "Unable to remove interface: Not there");
07782       break;
07783    case RES_NOSUCHQUEUE:
07784       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07785       break;
07786    case RES_OUTOFMEMORY:
07787       astman_send_error(s, m, "Out of memory");
07788       break;
07789    case RES_NOT_DYNAMIC:
07790       astman_send_error(s, m, "Member not dynamic");
07791       break;
07792    }
07793 
07794    return 0;
07795 }

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

Definition at line 7064 of file app_queue.c.

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

Referenced by reload_queues().

07065 {
07066    struct call_queue *q = obj;
07067    char *queuename = arg;
07068    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07069       q->dead = 1;
07070       q->found = 0;
07071    }
07072    return 0;
07073 }

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

Definition at line 6922 of file app_queue.c.

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

Referenced by reload_single_queue().

06923 {
06924    struct member *member = obj;
06925    if (!member->dynamic) {
06926       member->delme = 1;
06927    }
06928    return 0;
06929 }

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

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

02144 {
02145    ao2_lock(queue->members);
02146    mem->queuepos = ao2_container_count(queue->members);
02147    ao2_link(queue->members, mem);
02148    ao2_unlock(queue->members);
02149 }

static void member_call_pending_clear ( struct member mem  )  [static]

Definition at line 3104 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry(), and ring_entry().

03105 {
03106    ao2_lock(mem);
03107    mem->call_pending = 0;
03108    ao2_unlock(mem);
03109 }

static int member_call_pending_set ( struct member mem  )  [static]

Definition at line 3119 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry().

03120 {
03121    int old_pending;
03122 
03123    ao2_lock(mem);
03124    old_pending = mem->call_pending;
03125    mem->call_pending = 1;
03126    ao2_unlock(mem);
03127 
03128    return old_pending;
03129 }

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

Definition at line 1706 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

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

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

Definition at line 1694 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

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

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

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

02158 {
02159    ao2_lock(queue->members);
02160    queue_member_follower_removal(queue, mem);
02161    ao2_unlink(queue->members, mem);
02162    ao2_unlock(queue->members);
02163 }

static int member_status_available ( int  status  )  [static]

Definition at line 3091 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.

Referenced by can_ring_entry(), and manager_queues_summary().

03092 {
03093    return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN;
03094 }

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

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

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

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

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

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

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

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

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

PauseQueueMember application.

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

05830 {
05831    char *parse;
05832    AST_DECLARE_APP_ARGS(args,
05833       AST_APP_ARG(queuename);
05834       AST_APP_ARG(interface);
05835       AST_APP_ARG(options);
05836       AST_APP_ARG(reason);
05837    );
05838 
05839    if (ast_strlen_zero(data)) {
05840       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05841       return -1;
05842    }
05843 
05844    parse = ast_strdupa(data);
05845 
05846    AST_STANDARD_APP_ARGS(args, parse);
05847 
05848    if (ast_strlen_zero(args.interface)) {
05849       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05850       return -1;
05851    }
05852 
05853    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05854       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05855       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05856       return 0;
05857    }
05858 
05859    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05860 
05861    return 0;
05862 }

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

QueueLog application.

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

06021 {
06022    char *parse;
06023 
06024    AST_DECLARE_APP_ARGS(args,
06025       AST_APP_ARG(queuename);
06026       AST_APP_ARG(uniqueid);
06027       AST_APP_ARG(membername);
06028       AST_APP_ARG(event);
06029       AST_APP_ARG(params);
06030    );
06031 
06032    if (ast_strlen_zero(data)) {
06033       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
06034       return -1;
06035    }
06036 
06037    parse = ast_strdupa(data);
06038 
06039    AST_STANDARD_APP_ARGS(args, parse);
06040 
06041    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
06042        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
06043       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
06044       return -1;
06045    }
06046 
06047    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
06048       "%s", args.params ? args.params : "");
06049 
06050    return 0;
06051 }

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

Definition at line 1259 of file app_queue.c.

References CMP_MATCH, and CMP_STOP.

Referenced by load_module().

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

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

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

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

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

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

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

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

06441 {
06442    struct call_queue *q;
06443 
06444    buf[0] = '\0';
06445 
06446    if (ast_strlen_zero(data)) {
06447       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06448       return -1;
06449    }
06450    q = load_realtime_queue(data);
06451    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06452    if (q) {
06453       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06454    }
06455 
06456    return 0;
06457 }

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

06660 {
06661    int penalty;
06662    AST_DECLARE_APP_ARGS(args,
06663       AST_APP_ARG(queuename);
06664       AST_APP_ARG(interface);
06665    );
06666    /* Make sure the returned value on error is NULL. */
06667    buf[0] = '\0';
06668 
06669    if (ast_strlen_zero(data)) {
06670       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06671       return -1;
06672    }
06673 
06674    AST_STANDARD_APP_ARGS(args, data);
06675 
06676    if (args.argc < 2) {
06677       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06678       return -1;
06679    }
06680 
06681    penalty = get_member_penalty (args.queuename, args.interface);
06682    
06683    if (penalty >= 0) /* remember that buf is already '\0' */
06684       snprintf (buf, len, "%d", penalty);
06685 
06686    return 0;
06687 }

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

06691 {
06692    int penalty;
06693    AST_DECLARE_APP_ARGS(args,
06694       AST_APP_ARG(queuename);
06695       AST_APP_ARG(interface);
06696    );
06697 
06698    if (ast_strlen_zero(data)) {
06699       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06700       return -1;
06701    }
06702 
06703    AST_STANDARD_APP_ARGS(args, data);
06704 
06705    if (args.argc < 2) {
06706       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06707       return -1;
06708    }
06709 
06710    penalty = atoi(value);
06711 
06712    if (ast_strlen_zero(args.interface)) {
06713       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06714       return -1;
06715    }
06716 
06717    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06718    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06719       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06720       return -1;
06721    }
06722 
06723    return 0;
06724 }

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

06465 {
06466    int count = 0;
06467    struct member *m;
06468    struct ao2_iterator mem_iter;
06469    struct call_queue *q;
06470    char *option;
06471 
06472    if (ast_strlen_zero(data)) {
06473       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06474       return -1;
06475    }
06476 
06477    if ((option = strchr(data, ',')))
06478       *option++ = '\0';
06479    else
06480       option = "logged";
06481    if ((q = load_realtime_queue(data))) {
06482       ao2_lock(q);
06483       if (!strcasecmp(option, "logged")) {
06484          mem_iter = ao2_iterator_init(q->members, 0);
06485          while ((m = ao2_iterator_next(&mem_iter))) {
06486             /* Count the agents who are logged in and presently answering calls */
06487             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06488                count++;
06489             }
06490             ao2_ref(m, -1);
06491          }
06492          ao2_iterator_destroy(&mem_iter);
06493       } else if (!strcasecmp(option, "free")) {
06494          mem_iter = ao2_iterator_init(q->members, 0);
06495          while ((m = ao2_iterator_next(&mem_iter))) {
06496             /* Count the agents who are logged in and presently answering calls */
06497             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06498                count++;
06499             }
06500             ao2_ref(m, -1);
06501          }
06502          ao2_iterator_destroy(&mem_iter);
06503       } else if (!strcasecmp(option, "ready")) {
06504          time_t now;
06505          time(&now);
06506          mem_iter = ao2_iterator_init(q->members, 0);
06507          while ((m = ao2_iterator_next(&mem_iter))) {
06508             /* Count the agents who are logged in, not paused and not wrapping up */
06509             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06510                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06511                count++;
06512             }
06513             ao2_ref(m, -1);
06514          }
06515          ao2_iterator_destroy(&mem_iter);
06516       } else /* must be "count" */
06517          count = ao2_container_count(q->members);
06518       ao2_unlock(q);
06519       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06520    } else
06521       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06522 
06523    snprintf(buf, len, "%d", count);
06524 
06525    return 0;
06526 }

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

06534 {
06535    int count = 0;
06536    struct member *m;
06537    struct call_queue *q;
06538    struct ao2_iterator mem_iter;
06539    static int depflag = 1;
06540 
06541    if (depflag) {
06542       depflag = 0;
06543       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");
06544    }
06545 
06546    if (ast_strlen_zero(data)) {
06547       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06548       return -1;
06549    }
06550    
06551    if ((q = load_realtime_queue(data))) {
06552       ao2_lock(q);
06553       mem_iter = ao2_iterator_init(q->members, 0);
06554       while ((m = ao2_iterator_next(&mem_iter))) {
06555          /* Count the agents who are logged in and presently answering calls */
06556          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06557             count++;
06558          }
06559          ao2_ref(m, -1);
06560       }
06561       ao2_iterator_destroy(&mem_iter);
06562       ao2_unlock(q);
06563       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06564    } else
06565       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06566 
06567    snprintf(buf, len, "%d", count);
06568 
06569    return 0;
06570 }

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

06610 {
06611    struct call_queue *q, tmpq = {
06612       .name = data,  
06613    };
06614    struct member *m;
06615 
06616    /* Ensure an otherwise empty list doesn't return garbage */
06617    buf[0] = '\0';
06618 
06619    if (ast_strlen_zero(data)) {
06620       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06621       return -1;
06622    }
06623 
06624    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06625       int buflen = 0, count = 0;
06626       struct ao2_iterator mem_iter;
06627 
06628       ao2_lock(q);
06629       mem_iter = ao2_iterator_init(q->members, 0);
06630       while ((m = ao2_iterator_next(&mem_iter))) {
06631          /* strcat() is always faster than printf() */
06632          if (count++) {
06633             strncat(buf + buflen, ",", len - buflen - 1);
06634             buflen++;
06635          }
06636          strncat(buf + buflen, m->interface, len - buflen - 1);
06637          buflen += strlen(m->interface);
06638          /* Safeguard against overflow (negative length) */
06639          if (buflen >= len - 2) {
06640             ao2_ref(m, -1);
06641             ast_log(LOG_WARNING, "Truncating list\n");
06642             break;
06643          }
06644          ao2_ref(m, -1);
06645       }
06646       ao2_iterator_destroy(&mem_iter);
06647       ao2_unlock(q);
06648       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06649    } else
06650       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06651 
06652    /* We should already be terminated, but let's make sure. */
06653    buf[len - 1] = '\0';
06654 
06655    return 0;
06656 }

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

06574 {
06575    int count = 0;
06576    struct call_queue *q, tmpq = {
06577       .name = data,  
06578    };
06579    struct ast_variable *var = NULL;
06580 
06581    buf[0] = '\0';
06582    
06583    if (ast_strlen_zero(data)) {
06584       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06585       return -1;
06586    }
06587 
06588    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06589       ao2_lock(q);
06590       count = q->count;
06591       ao2_unlock(q);
06592       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06593    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06594       /* if the queue is realtime but was not found in memory, this
06595        * means that the queue had been deleted from memory since it was 
06596        * "dead." This means it has a 0 waiting count
06597        */
06598       count = 0;
06599       ast_variables_destroy(var);
06600    } else
06601       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06602 
06603    snprintf(buf, len, "%d", count);
06604 
06605    return 0;
06606 }

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

06394 {
06395    int res = -1;
06396    struct call_queue *q, tmpq = {
06397       .name = data,  
06398    };
06399 
06400    char interfacevar[256] = "";
06401    float sl = 0;
06402 
06403    if (ast_strlen_zero(data)) {
06404       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06405       return -1;
06406    }
06407 
06408    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06409       ao2_lock(q);
06410       if (q->setqueuevar) {
06411          sl = 0;
06412          res = 0;
06413 
06414          if (q->callscompleted > 0) {
06415             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06416          }
06417 
06418          snprintf(interfacevar, sizeof(interfacevar),
06419             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06420             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06421 
06422          pbx_builtin_setvar_multiple(chan, interfacevar);
06423       }
06424 
06425       ao2_unlock(q);
06426       queue_t_unref(q, "Done with QUEUE() function");
06427    } else {
06428       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06429    }
06430 
06431    snprintf(buf, len, "%d", res);
06432 
06433    return 0;
06434 }

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

Definition at line 1252 of file app_queue.c.

References ast_str_case_hash().

Referenced by load_module().

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

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

Definition at line 1270 of file app_queue.c.

References member::queuepos.

Referenced by queue_delme_members_decrement_followers(), and queue_member_follower_removal().

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

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

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

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

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

Definition at line 1331 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

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

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06818 {
06819    const char *general_val = NULL;
06820    queue_persistent_members = 0;
06821    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06822       queue_persistent_members = ast_true(general_val);
06823    autofill_default = 0;
06824    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06825       autofill_default = ast_true(general_val);
06826    montype_default = 0;
06827    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06828       if (!strcasecmp(general_val, "mixmonitor"))
06829          montype_default = 1;
06830    }
06831    update_cdr = 0;
06832    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06833       update_cdr = ast_true(general_val);
06834    shared_lastcall = 0;
06835    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06836       shared_lastcall = ast_true(general_val);
06837 }

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

References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, autopause2int(), queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, 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().

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

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

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

07473 {
07474    switch ( cmd ) {
07475    case CLI_INIT:
07476       e->command = "queue show";
07477       e->usage =
07478          "Usage: queue show\n"
07479          "       Provides summary information on a specified queue.\n";
07480       return NULL;
07481    case CLI_GENERATE:
07482       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07483    }
07484 
07485    return __queues_show(NULL, a->fd, a->argc, a->argv);
07486 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4451 of file app_queue.c.

References ast_free.

04452 {
04453    struct queue_transfer_ds *qtds = data;
04454    ast_free(qtds);
04455 }

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

04475 {
04476    struct queue_transfer_ds *qtds = data;
04477    struct queue_ent *qe = qtds->qe;
04478    struct member *member = qtds->member;
04479    time_t callstart = qtds->starttime;
04480    int callcompletedinsl = qtds->callcompletedinsl;
04481    struct ast_datastore *datastore;
04482 
04483    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04484             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04485             (long) (time(NULL) - callstart), qe->opos);
04486 
04487    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04488    
04489    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04490    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04491       ast_channel_datastore_remove(old_chan, datastore);
04492    } else {
04493       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04494    }
04495 }

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

Definition at line 1337 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec(), and queues_data_provider_get().

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

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

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

08642 {
08643    struct ao2_iterator i;
08644    struct call_queue *queue, *queue_realtime = NULL;
08645    struct ast_config *cfg;
08646    char *queuename;
08647 
08648    /* load realtime queues. */
08649    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08650    if (cfg) {
08651       for (queuename = ast_category_browse(cfg, NULL);
08652             !ast_strlen_zero(queuename);
08653             queuename = ast_category_browse(cfg, queuename)) {
08654          if ((queue = load_realtime_queue(queuename))) {
08655             queue_unref(queue);
08656          }
08657       }
08658       ast_config_destroy(cfg);
08659    }
08660 
08661    /* static queues. */
08662    i = ao2_iterator_init(queues, 0);
08663    while ((queue = ao2_iterator_next(&i))) {
08664       ao2_lock(queue);
08665       if (queue->realtime) {
08666          queue_realtime = load_realtime_queue(queue->name);
08667          if (!queue_realtime) {
08668             ao2_unlock(queue);
08669             queue_unref(queue);
08670             continue;
08671          }
08672          queue_unref(queue_realtime);
08673       }
08674 
08675       queues_data_provider_get_helper(search, data_root, queue);
08676       ao2_unlock(queue);
08677       queue_unref(queue);
08678    }
08679    ao2_iterator_destroy(&i);
08680 
08681    return 0;
08682 }

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

08536 {
08537    struct ao2_iterator im;
08538    struct member *member;
08539    struct queue_ent *qe;
08540    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08541    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08542 
08543    data_queue = ast_data_add_node(data_root, "queue");
08544    if (!data_queue) {
08545       return;
08546    }
08547 
08548    ast_data_add_structure(call_queue, data_queue, queue);
08549 
08550    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08551    ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08552 
08553    /* announce position */
08554    enum_node = ast_data_add_node(data_queue, "announceposition");
08555    if (!enum_node) {
08556       return;
08557    }
08558    switch (queue->announceposition) {
08559    case ANNOUNCEPOSITION_LIMIT:
08560       ast_data_add_str(enum_node, "text", "limit");
08561       break;
08562    case ANNOUNCEPOSITION_MORE_THAN:
08563       ast_data_add_str(enum_node, "text", "more");
08564       break;
08565    case ANNOUNCEPOSITION_YES:
08566       ast_data_add_str(enum_node, "text", "yes");
08567       break;
08568    case ANNOUNCEPOSITION_NO:
08569       ast_data_add_str(enum_node, "text", "no");
08570       break;
08571    default:
08572       ast_data_add_str(enum_node, "text", "unknown");
08573       break;
08574    }
08575    ast_data_add_int(enum_node, "value", queue->announceposition);
08576 
08577    /* add queue members */
08578    im = ao2_iterator_init(queue->members, 0);
08579    while ((member = ao2_iterator_next(&im))) {
08580       if (!data_members) {
08581          data_members = ast_data_add_node(data_queue, "members");
08582          if (!data_members) {
08583             ao2_ref(member, -1);
08584             continue;
08585          }
08586       }
08587 
08588       data_member = ast_data_add_node(data_members, "member");
08589       if (!data_member) {
08590          ao2_ref(member, -1);
08591          continue;
08592       }
08593 
08594       ast_data_add_structure(member, data_member, member);
08595 
08596       ao2_ref(member, -1);
08597    }
08598    ao2_iterator_destroy(&im);
08599 
08600    /* include the callers inside the result. */
08601    if (queue->head) {
08602       for (qe = queue->head; qe; qe = qe->next) {
08603          if (!data_callers) {
08604             data_callers = ast_data_add_node(data_queue, "callers");
08605             if (!data_callers) {
08606                continue;
08607             }
08608          }
08609 
08610          data_caller = ast_data_add_node(data_callers, "caller");
08611          if (!data_caller) {
08612             continue;
08613          }
08614 
08615          ast_data_add_structure(queue_ent, data_caller, qe);
08616 
08617          /* add the caller channel. */
08618          data_caller_channel = ast_data_add_node(data_caller, "channel");
08619          if (!data_caller_channel) {
08620             continue;
08621          }
08622 
08623          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08624       }
08625    }
08626 
08627    /* if this queue doesn't match remove the added queue. */
08628    if (!ast_data_search_match(search, data_queue)) {
08629       ast_data_remove_node(data_root, data_queue);
08630    }
08631 }

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

Definition at line 2836 of file app_queue.c.

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

Referenced by try_calling().

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

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

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

03525 {
03526    set_queue_variables(qe->parent, qe->chan);
03527    ao2_lock(qe->parent);
03528    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03529       "Queue: %s\r\n"
03530       "Uniqueid: %s\r\n"
03531       "Position: %d\r\n"
03532       "OriginalPosition: %d\r\n"
03533       "HoldTime: %d\r\n",
03534       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03535 
03536    qe->parent->callsabandoned++;
03537    ao2_unlock(qe->parent);
03538 }

static int reload ( void   )  [static]

Definition at line 8814 of file app_queue.c.

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

08815 {
08816    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08817    ast_unload_realtime("queue_members");
08818    reload_handler(1, &mask, NULL);
08819    return 0;
08820 }

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

07194 {
07195    int res = 0;
07196 
07197    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
07198       res |= reload_queue_rules(reload);
07199    }
07200    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
07201       res |= clear_stats(queuename);
07202    }
07203    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
07204       res |= reload_queues(reload, mask, queuename);
07205    }
07206    return res;
07207 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

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

05735 {
05736    char *cur_ptr;
05737    const char *queue_name;
05738    char *member;
05739    char *interface;
05740    char *membername = NULL;
05741    char *state_interface;
05742    char *penalty_tok;
05743    int penalty = 0;
05744    char *paused_tok;
05745    int paused = 0;
05746    struct ast_db_entry *db_tree;
05747    struct ast_db_entry *entry;
05748    struct call_queue *cur_queue;
05749    char *queue_data;
05750 
05751    /* Each key in 'pm_family' is the name of a queue */
05752    db_tree = ast_db_gettree(pm_family, NULL);
05753    for (entry = db_tree; entry; entry = entry->next) {
05754 
05755       queue_name = entry->key + strlen(pm_family) + 2;
05756 
05757       {
05758          struct call_queue tmpq = {
05759             .name = queue_name,
05760          };
05761          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05762       }  
05763 
05764       if (!cur_queue)
05765          cur_queue = load_realtime_queue(queue_name);
05766 
05767       if (!cur_queue) {
05768          /* If the queue no longer exists, remove it from the
05769           * database */
05770          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05771          ast_db_del(pm_family, queue_name);
05772          continue;
05773       } 
05774 
05775       if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
05776          queue_t_unref(cur_queue, "Expire reload reference");
05777          continue;
05778       }
05779 
05780       cur_ptr = queue_data;
05781       while ((member = strsep(&cur_ptr, ",|"))) {
05782          if (ast_strlen_zero(member))
05783             continue;
05784 
05785          interface = strsep(&member, ";");
05786          penalty_tok = strsep(&member, ";");
05787          paused_tok = strsep(&member, ";");
05788          membername = strsep(&member, ";");
05789          state_interface = strsep(&member, ";");
05790 
05791          if (!penalty_tok) {
05792             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05793             break;
05794          }
05795          penalty = strtol(penalty_tok, NULL, 10);
05796          if (errno == ERANGE) {
05797             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05798             break;
05799          }
05800          
05801          if (!paused_tok) {
05802             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05803             break;
05804          }
05805          paused = strtol(paused_tok, NULL, 10);
05806          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05807             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05808             break;
05809          }
05810 
05811          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05812          
05813          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05814             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05815             break;
05816          }
05817       }
05818       queue_t_unref(cur_queue, "Expire reload reference");
05819       ast_free(queue_data);
05820    }
05821 
05822    if (db_tree) {
05823       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05824       ast_db_freetree(db_tree);
05825    }
05826 }

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

06769 {
06770    struct ast_config *cfg;
06771    struct rule_list *rl_iter, *new_rl;
06772    struct penalty_rule *pr_iter;
06773    char *rulecat = NULL;
06774    struct ast_variable *rulevar = NULL;
06775    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06776    
06777    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06778       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06779       return AST_MODULE_LOAD_SUCCESS;
06780    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06781       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06782       return AST_MODULE_LOAD_SUCCESS;
06783    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06784       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
06785       return AST_MODULE_LOAD_SUCCESS;
06786    }
06787 
06788    AST_LIST_LOCK(&rule_lists);
06789    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06790       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06791          ast_free(pr_iter);
06792       ast_free(rl_iter);
06793    }
06794    while ((rulecat = ast_category_browse(cfg, rulecat))) {
06795       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06796          AST_LIST_UNLOCK(&rule_lists);
06797          ast_config_destroy(cfg);
06798          return AST_MODULE_LOAD_FAILURE;
06799       } else {
06800          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06801          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06802          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06803             if(!strcasecmp(rulevar->name, "penaltychange"))
06804                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06805             else
06806                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06807       }
06808    }
06809    AST_LIST_UNLOCK(&rule_lists);
06810 
06811    ast_config_destroy(cfg);
06812 
06813    return AST_MODULE_LOAD_SUCCESS;
06814 }

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

07099 {
07100    struct ast_config *cfg;
07101    char *cat;
07102    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07103    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
07104    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
07105 
07106    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
07107       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
07108       return -1;
07109    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07110       return 0;
07111    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07112       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
07113       return -1;
07114    }
07115 
07116    /* We've made it here, so it looks like we're doing operations on all queues. */
07117    ao2_lock(queues);
07118 
07119    /* Mark all queues as dead for the moment if we're reloading queues.
07120     * For clarity, we could just be reloading members, in which case we don't want to mess
07121     * with the other queue parameters at all*/
07122    if (queue_reload) {
07123       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
07124    }
07125 
07126    if (member_reload) {
07127       ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename);
07128    }
07129 
07130    /* Chug through config file */
07131    cat = NULL;
07132    while ((cat = ast_category_browse(cfg, cat)) ) {
07133       if (!strcasecmp(cat, "general") && queue_reload) {
07134          queue_set_global_params(cfg);
07135          continue;
07136       }
07137       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
07138          reload_single_queue(cfg, mask, cat);
07139    }
07140 
07141    ast_config_destroy(cfg);
07142    /* Unref all the dead queues if we were reloading queues */
07143    if (queue_reload) {
07144       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
07145    }
07146    ao2_unlock(queues);
07147    return 0;
07148 }

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

06848 {
06849    char *membername, *interface, *state_interface, *tmp;
06850    char *parse;
06851    struct member *cur, *newm;
06852    struct member tmpmem;
06853    int penalty;
06854    AST_DECLARE_APP_ARGS(args,
06855       AST_APP_ARG(interface);
06856       AST_APP_ARG(penalty);
06857       AST_APP_ARG(membername);
06858       AST_APP_ARG(state_interface);
06859    );
06860 
06861    if (ast_strlen_zero(memberdata)) {
06862       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06863       return;
06864    }
06865 
06866    /* Add a new member */
06867    parse = ast_strdupa(memberdata);
06868             
06869    AST_STANDARD_APP_ARGS(args, parse);
06870 
06871    interface = args.interface;
06872    if (!ast_strlen_zero(args.penalty)) {
06873       tmp = args.penalty;
06874       ast_strip(tmp);
06875       penalty = atoi(tmp);
06876       if (penalty < 0) {
06877          penalty = 0;
06878       }
06879    } else {
06880       penalty = 0;
06881    }
06882 
06883    if (!ast_strlen_zero(args.membername)) {
06884       membername = args.membername;
06885       ast_strip(membername);
06886    } else {
06887       membername = interface;
06888    }
06889 
06890    if (!ast_strlen_zero(args.state_interface)) {
06891       state_interface = args.state_interface;
06892       ast_strip(state_interface);
06893    } else {
06894       state_interface = interface;
06895    }
06896 
06897    /* Find the old position in the list */
06898    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06899    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
06900 
06901    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06902       if (cur) {
06903          /* Round Robin Queue Position must be copied if this is replacing an existing member */
06904          ao2_lock(q->members);
06905          newm->queuepos = cur->queuepos;
06906          ao2_link(q->members, newm);
06907          ao2_unlink(q->members, cur);
06908          ao2_unlock(q->members);
06909       } else {
06910          /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
06911          member_add_to_queue(q, newm);
06912       }
06913       ao2_ref(newm, -1);
06914    }
06915    newm = NULL;
06916 
06917    if (cur) {
06918       ao2_ref(cur, -1);
06919    }
06920 }

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

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

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

05478 {
05479    struct call_queue *q, tmpq = {
05480       .name = queuename,   
05481    };
05482    struct member *mem, tmpmem;
05483    int res = RES_NOSUCHQUEUE;
05484 
05485    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05486    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05487       ao2_lock(q);
05488       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05489          /* XXX future changes should beware of this assumption!! */
05490          if (!mem->dynamic) {
05491             ao2_ref(mem, -1);
05492             ao2_unlock(q);
05493             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05494             return RES_NOT_DYNAMIC;
05495          }
05496          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05497             "Queue: %s\r\n"
05498             "Location: %s\r\n"
05499             "MemberName: %s\r\n",
05500             q->name, mem->interface, mem->membername);
05501          member_remove_from_queue(q, mem);
05502          ao2_ref(mem, -1);
05503 
05504          if (queue_persistent_members)
05505             dump_queue_members(q);
05506          
05507          res = RES_OKAY;
05508       } else {
05509          res = RES_EXISTS;
05510       }
05511       ao2_unlock(q);
05512       queue_t_unref(q, "Expiring temporary reference");
05513    }
05514 
05515    return res;
05516 }

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

Definition at line 7053 of file app_queue.c.

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

Referenced by reload_queues().

07054 {
07055    struct call_queue *q = obj;
07056    char *queuename = arg;
07057    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07058       q->found = 0;
07059 
07060    }
07061    return 0;
07062 }

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 3203 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, queue_ent::chan, callattempt::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().

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

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

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

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

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

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

RemoveQueueMember application.

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

05902 {
05903    int res=-1;
05904    char *parse, *temppos = NULL;
05905    AST_DECLARE_APP_ARGS(args,
05906       AST_APP_ARG(queuename);
05907       AST_APP_ARG(interface);
05908    );
05909 
05910 
05911    if (ast_strlen_zero(data)) {
05912       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
05913       return -1;
05914    }
05915 
05916    parse = ast_strdupa(data);
05917 
05918    AST_STANDARD_APP_ARGS(args, parse);
05919 
05920    if (ast_strlen_zero(args.interface)) {
05921       args.interface = ast_strdupa(chan->name);
05922       temppos = strrchr(args.interface, '-');
05923       if (temppos)
05924          *temppos = '\0';
05925    }
05926 
05927    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05928 
05929    switch (remove_from_queue(args.queuename, args.interface)) {
05930    case RES_OKAY:
05931       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05932       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05933       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05934       res = 0;
05935       break;
05936    case RES_EXISTS:
05937       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05938       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05939       res = 0;
05940       break;
05941    case RES_NOSUCHQUEUE:
05942       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05943       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05944       res = 0;
05945       break;
05946    case RES_NOT_DYNAMIC:
05947       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05948       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05949       res = 0;
05950       break;
05951    }
05952 
05953    return res;
05954 }

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

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

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

Playback announcement to queued members if period has elapsed.

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

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

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

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

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

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

04411 {
04412    const char *reason = NULL; /* silence dumb compilers */
04413 
04414    if (!qe->parent->eventwhencalled)
04415       return;
04416 
04417    switch (rsn) {
04418    case CALLER:
04419       reason = "caller";
04420       break;
04421    case AGENT:
04422       reason = "agent";
04423       break;
04424    case TRANSFER:
04425       reason = "transfer";
04426       break;
04427    }
04428 
04429    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04430       "Queue: %s\r\n"
04431       "Uniqueid: %s\r\n"
04432       "Channel: %s\r\n"
04433       "Member: %s\r\n"
04434       "MemberName: %s\r\n"
04435       "HoldTime: %ld\r\n"
04436       "TalkTime: %ld\r\n"
04437       "Reason: %s\r\n"
04438       "%s",
04439       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04440       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04441       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04442 }

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

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

05577 {
05578    int found = 0;
05579    struct call_queue *q;
05580    struct member *mem;
05581    struct ao2_iterator queue_iter;
05582    int failed;
05583 
05584    /* Special event for when all queues are paused - individual events still generated */
05585    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05586    if (ast_strlen_zero(queuename))
05587       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05588 
05589    queue_iter = ao2_iterator_init(queues, 0);
05590    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05591       ao2_lock(q);
05592       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05593          if ((mem = interface_exists(q, interface))) {
05594             if (mem->paused == paused) {
05595                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05596             }
05597 
05598             failed = 0;
05599             if (mem->realtime) {
05600                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05601             }
05602          
05603             if (failed) {
05604                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05605                ao2_ref(mem, -1);
05606                ao2_unlock(q);
05607                queue_t_unref(q, "Done with iterator");
05608                continue;
05609             }  
05610             found++;
05611             mem->paused = paused;
05612 
05613             if (queue_persistent_members)
05614                dump_queue_members(q);
05615 
05616             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05617             
05618             if (!ast_strlen_zero(reason)) {
05619                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05620                   "Queue: %s\r\n"
05621                   "Location: %s\r\n"
05622                   "MemberName: %s\r\n"
05623                   "Paused: %d\r\n"
05624                   "Reason: %s\r\n",
05625                      q->name, mem->interface, mem->membername, paused, reason);
05626             } else {
05627                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05628                   "Queue: %s\r\n"
05629                   "Location: %s\r\n"
05630                   "MemberName: %s\r\n"
05631                   "Paused: %d\r\n",
05632                      q->name, mem->interface, mem->membername, paused);
05633             }
05634             ao2_ref(mem, -1);
05635          }
05636       }
05637       
05638       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05639          ao2_unlock(q);
05640          queue_t_unref(q, "Done with iterator");
05641          break;
05642       }
05643       
05644       ao2_unlock(q);
05645       queue_t_unref(q, "Done with iterator");
05646    }
05647    ao2_iterator_destroy(&queue_iter);
05648 
05649    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05650 }

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

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

05654 {
05655    int foundinterface = 0, foundqueue = 0;
05656    struct call_queue *q;
05657    struct member *mem;
05658    struct ao2_iterator queue_iter;
05659 
05660    if (penalty < 0) {
05661       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05662       return RESULT_FAILURE;
05663    }
05664 
05665    queue_iter = ao2_iterator_init(queues, 0);
05666    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05667       ao2_lock(q);
05668       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05669          foundqueue++;
05670          if ((mem = interface_exists(q, interface))) {
05671             foundinterface++;
05672             mem->penalty = penalty;
05673             
05674             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05675             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05676                "Queue: %s\r\n"
05677                "Location: %s\r\n"
05678                "Penalty: %d\r\n",
05679                q->name, mem->interface, penalty);
05680             ao2_ref(mem, -1);
05681          }
05682       }
05683       ao2_unlock(q);
05684       queue_t_unref(q, "Done with iterator");
05685    }
05686    ao2_iterator_destroy(&queue_iter);
05687 
05688    if (foundinterface) {
05689       return RESULT_SUCCESS;
05690    } else if (!foundqueue) {
05691       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05692    } else {
05693       ast_log (LOG_ERROR, "Invalid interface\n");
05694    }  
05695 
05696    return RESULT_FAILURE;
05697 }

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

sets the QUEUESTATUS channel variable

Definition at line 1196 of file app_queue.c.

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

Referenced by queue_exec().

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

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

Set variables of queue.

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

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

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

04513 {
04514    struct ast_datastore *ds;
04515    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04516 
04517    if (!qtds) {
04518       ast_log(LOG_WARNING, "Memory allocation error!\n");
04519       return NULL;
04520    }
04521 
04522    ast_channel_lock(qe->chan);
04523    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04524       ast_channel_unlock(qe->chan);
04525       ast_free(qtds);
04526       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04527       return NULL;
04528    }
04529 
04530    qtds->qe = qe;
04531    /* This member is refcounted in try_calling, so no need to add it here, too */
04532    qtds->member = member;
04533    qtds->starttime = starttime;
04534    qtds->callcompletedinsl = callcompletedinsl;
04535    ds->data = qtds;
04536    ast_channel_datastore_add(qe->chan, ds);
04537    ast_channel_unlock(qe->chan);
04538    return ds;
04539 }

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

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

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

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

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

Definition at line 1220 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

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

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

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

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

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

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

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

static int unload_module ( void   )  [static]

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

08694 {
08695    int res;
08696    struct ast_context *con;
08697    struct ao2_iterator q_iter;
08698    struct call_queue *q = NULL;
08699 
08700    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08701    res = ast_manager_unregister("QueueStatus");
08702    res |= ast_manager_unregister("Queues");
08703    res |= ast_manager_unregister("QueueRule");
08704    res |= ast_manager_unregister("QueueSummary");
08705    res |= ast_manager_unregister("QueueAdd");
08706    res |= ast_manager_unregister("QueueRemove");
08707    res |= ast_manager_unregister("QueuePause");
08708    res |= ast_manager_unregister("QueueLog");
08709    res |= ast_manager_unregister("QueuePenalty");
08710    res |= ast_unregister_application(app_aqm);
08711    res |= ast_unregister_application(app_rqm);
08712    res |= ast_unregister_application(app_pqm);
08713    res |= ast_unregister_application(app_upqm);
08714    res |= ast_unregister_application(app_ql);
08715    res |= ast_unregister_application(app);
08716    res |= ast_custom_function_unregister(&queueexists_function);
08717    res |= ast_custom_function_unregister(&queuevar_function);
08718    res |= ast_custom_function_unregister(&queuemembercount_function);
08719    res |= ast_custom_function_unregister(&queuemembercount_dep);
08720    res |= ast_custom_function_unregister(&queuememberlist_function);
08721    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08722    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08723 
08724    res |= ast_data_unregister(NULL);
08725 
08726    if (device_state_sub)
08727       ast_event_unsubscribe(device_state_sub);
08728 
08729    ast_extension_state_del(0, extension_state_cb);
08730 
08731    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08732       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08733       ast_context_destroy(con, "app_queue"); /* leave no trace */
08734    }
08735 
08736    q_iter = ao2_iterator_init(queues, 0);
08737    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08738       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08739       queue_t_unref(q, "Done with iterator");
08740    }
08741    ao2_iterator_destroy(&q_iter);
08742    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08743    ao2_ref(queues, -1);
08744    ast_unload_realtime("queue_members");
08745    return res;
08746 }

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

04176 {
04177    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
04178    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
04179    char max_penalty_str[20], min_penalty_str[20]; 
04180    /* a relative change to the penalty could put it below 0 */
04181    if (max_penalty < 0)
04182       max_penalty = 0;
04183    if (min_penalty < 0)
04184       min_penalty = 0;
04185    if (min_penalty > max_penalty)
04186       min_penalty = max_penalty;
04187    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
04188    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
04189    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
04190    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
04191    qe->max_penalty = max_penalty;
04192    qe->min_penalty = min_penalty;
04193    ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
04194    qe->pr = AST_LIST_NEXT(qe->pr, list);
04195 }

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

04284 {
04285    int oldtalktime;
04286 
04287    struct member *mem;
04288    struct call_queue *qtmp;
04289    struct ao2_iterator queue_iter;  
04290    
04291    if (shared_lastcall) {
04292       queue_iter = ao2_iterator_init(queues, 0);
04293       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04294          ao2_lock(qtmp);
04295          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04296             time(&mem->lastcall);
04297             mem->calls++;
04298             mem->lastqueue = q;
04299             ao2_ref(mem, -1);
04300          }
04301          ao2_unlock(qtmp);
04302          queue_t_unref(qtmp, "Done with iterator");
04303       }
04304       ao2_iterator_destroy(&queue_iter);
04305    } else {
04306       ao2_lock(q);
04307       time(&member->lastcall);
04308       member->calls++;
04309       member->lastqueue = q;
04310       ao2_unlock(q);
04311    }  
04312    ao2_lock(q);
04313    q->callscompleted++;
04314    if (callcompletedinsl)
04315       q->callscompletedinsl++;
04316    /* Calculate talktime using the same exponential average as holdtime code*/
04317    oldtalktime = q->talktime;
04318    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04319    ao2_unlock(q);
04320    return 0;
04321 }

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

Definition at line 2471 of file app_queue.c.

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

Referenced by set_member_paused().

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

static void update_realtime_members ( struct call_queue q  )  [static]

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

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

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

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

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

UnPauseQueueMember application.

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

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

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

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

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

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

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5387 of file app_queue.c.

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

Referenced by queue_exec().

05388 {
05389    /* Don't need to hold the lock while we setup the outgoing calls */
05390    int retrywait = qe->parent->retry * 1000;
05391 
05392    int res = ast_waitfordigit(qe->chan, retrywait);
05393    if (res > 0 && !valid_exit(qe, res))
05394       res = 0;
05395 
05396    return res;
05397 }

static struct callattempt* wait_for_answer ( struct queue_ent qe,
struct callattempt outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect,
int  forwardsallowed 
) [static, 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 3607 of file app_queue.c.

References ast_channel::_state, accountcode, callattempt::aoc_s_rate_list, ast_aoc_decode(), ast_aoc_destroy_decoded(), ast_aoc_destroy_encoded(), ast_aoc_encode(), ast_aoc_get_msg_type(), AST_AOC_S, ast_call(), ast_cdr_failed(), ast_cdr_noanswer(), AST_CEL_FORWARD, ast_cel_report_event(), ast_channel_connected_line_macro(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, AST_CHANNEL_NAME, ast_channel_redirecting_macro(), ast_channel_unlock, ast_channel_update_connected_line(), ast_channel_update_redirecting(), ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_connected_line_parse_data(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_WATCHERS, ast_moh_stop(), ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_party_connected_line_set(), ast_party_connected_line_set_init(), ast_party_number_free(), ast_party_number_init(), ast_party_redirecting_copy(), ast_party_redirecting_free(), ast_party_redirecting_init(), ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), ast_remaining_ms(), ast_request(), AST_STATE_UP, ast_strdup, ast_strdupa, ast_string_field_set, ast_strlen_zero(), ast_tvnow(), ast_verb, ast_waitfor_n(), callattempt::block_connected_update, callattempt::call_next, ast_channel::caller, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::connected, callattempt::connected, ast_channel::context, ast_frame::data, ast_frame::datalen, callattempt::dial_callerid_absent, ast_channel::dialed, do_hang(), ast_channel::exten, f, ast_frame::frametype, ast_party_redirecting::from, ast_channel::hangupcause, ast_party_caller::id, ast_frame_subclass::integer, callattempt::interface, member::interface, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, ast_channel::nativeformats, ast_party_id::number, queue_ent::parent, callattempt::pending_connected_update, 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, 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().

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

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

04208 {
04209    int res = 0;
04210 
04211    /* This is the holding pen for callers 2 through maxlen */
04212    for (;;) {
04213 
04214       if (is_our_turn(qe))
04215          break;
04216 
04217       /* If we have timed out, break out */
04218       if (qe->expire && (time(NULL) >= qe->expire)) {
04219          *reason = QUEUE_TIMEOUT;
04220          break;
04221       }
04222 
04223       if (qe->parent->leavewhenempty) {
04224          int status = 0;
04225 
04226          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
04227             *reason = QUEUE_LEAVEEMPTY;
04228             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04229             leave_queue(qe);
04230             break;
04231          }
04232       }
04233 
04234       /* Make a position announcement, if enabled */
04235       if (qe->parent->announcefrequency &&
04236          (res = say_position(qe,ringing)))
04237          break;
04238 
04239       /* If we have timed out, break out */
04240       if (qe->expire && (time(NULL) >= qe->expire)) {
04241          *reason = QUEUE_TIMEOUT;
04242          break;
04243       }
04244 
04245       /* Make a periodic announcement, if enabled */
04246       if (qe->parent->periodicannouncefrequency &&
04247          (res = say_periodic_announcement(qe,ringing)))
04248          break;
04249       
04250       /* see if we need to move to the next penalty level for this queue */
04251       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04252          update_qe_rule(qe);
04253       }
04254 
04255       /* If we have timed out, break out */
04256       if (qe->expire && (time(NULL) >= qe->expire)) {
04257          *reason = QUEUE_TIMEOUT;
04258          break;
04259       }
04260       
04261       /* Wait a second before checking again */
04262       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04263          if (res > 0 && !valid_exit(qe, res))
04264             res = 0;
04265          else
04266             break;
04267       }
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 
04276    return res;
04277 }

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

Referenced by complete_queue().

07368                                                             {
07369    int list_len, word_len = strlen(word);
07370    const char *find, *end_find, *end_list;
07371 
07372    /* strip whitespace from front */
07373    while (isspace(*list)) {
07374       list++;
07375    }
07376 
07377    while ((find = strstr(list, word))) {
07378       /* beginning of find starts inside another word? */
07379       if (find != list && *(find - 1) != ' ') {
07380          list = find;
07381          /* strip word from front */
07382          while (!isspace(*list) && *list != '\0') {
07383             list++;
07384          }
07385          /* strip whitespace from front */
07386          while (isspace(*list)) {
07387             list++;
07388          }
07389          continue;
07390       }
07391 
07392       /* end of find ends inside another word or at very end of list? */
07393       list_len = strlen(list);
07394       end_find = find + word_len;
07395       end_list = list + list_len;
07396       if (end_find == end_list || *end_find != ' ') {
07397          list = find;
07398          /* strip word from front */
07399          while (!isspace(*list) && *list != '\0') {
07400             list++;
07401          }
07402          /* strip whitespace from front */
07403          while (isspace(*list)) {
07404             list++;
07405          }
07406          continue;
07407       }
07408 
07409       /* terminating conditions satisfied, word at beginning or separated by ' ' */
07410       return 1;
07411    }
07412    
07413    return 0;
07414 }


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 8412 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 8403 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 8409 of file app_queue.c.

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

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

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

Definition at line 6726 of file app_queue.c.

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

Definition at line 6741 of file app_queue.c.

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

Definition at line 6736 of file app_queue.c.

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

Definition at line 6751 of file app_queue.c.

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

Definition at line 6756 of file app_queue.c.

struct ao2_container* queues [static]
Initial value:

Definition at line 8684 of file app_queue.c.

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

Definition at line 6731 of file app_queue.c.

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

Definition at line 6746 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 8406 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 20 Aug 2013 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1