Fri Apr 15 20:38:34 2016

Asterisk developer's documentation


app_queue.c File Reference

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

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

Go to the source code of this file.

Data Structures

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

Defines

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

Enumerations

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

Functions

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

Variables

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

Detailed Description

True call queues with optional send URL on answer.

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

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

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

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 1115 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 1116 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 1131 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 1130 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 1129 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 1128 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 3654 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 8511 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8576 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 937 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 943 of file app_queue.c.

Referenced by load_module().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 1117 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 1378 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 1380 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 1381 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 939 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 886 of file app_queue.c.

anonymous enum
Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 897 of file app_queue.c.

00897      {
00898      QUEUE_AUTOPAUSE_OFF = 0,
00899      QUEUE_AUTOPAUSE_ON,
00900      QUEUE_AUTOPAUSE_ALL
00901 };

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4485 of file app_queue.c.

04485                            {
04486    CALLER,
04487    AGENT,
04488    TRANSFER
04489 };

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

01103                       {
01104    QUEUE_EMPTY_PENALTY = (1 << 0),
01105    QUEUE_EMPTY_PAUSED = (1 << 1),
01106    QUEUE_EMPTY_INUSE = (1 << 2),
01107    QUEUE_EMPTY_RINGING = (1 << 3),
01108    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
01109    QUEUE_EMPTY_INVALID = (1 << 5),
01110    QUEUE_EMPTY_UNKNOWN = (1 << 6),
01111    QUEUE_EMPTY_WRAPUP = (1 << 7),
01112 };

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 903 of file app_queue.c.

00903                        {
00904    QUEUE_RELOAD_PARAMETERS = (1 << 0),
00905    QUEUE_RELOAD_MEMBER = (1 << 1),
00906    QUEUE_RELOAD_RULES = (1 << 2),
00907    QUEUE_RESET_STATS = (1 << 3),
00908 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 987 of file app_queue.c.

00987                   {
00988    QUEUE_UNKNOWN = 0,
00989    QUEUE_TIMEOUT = 1,
00990    QUEUE_JOINEMPTY = 2,
00991    QUEUE_LEAVEEMPTY = 3,
00992    QUEUE_JOINUNAVAIL = 4,
00993    QUEUE_LEAVEUNAVAIL = 5,
00994    QUEUE_FULL = 6,
00995    QUEUE_CONTINUE = 7,
00996 };

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 1012 of file app_queue.c.

01012                             {
01013    TIMEOUT_PRIORITY_APP,
01014    TIMEOUT_PRIORITY_CONF,
01015 };


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

07311 {
07312    struct call_queue *q;
07313    struct ast_str *out = ast_str_alloca(240);
07314    int found = 0;
07315    time_t now = time(NULL);
07316    struct ao2_iterator queue_iter;
07317    struct ao2_iterator mem_iter;
07318 
07319    if (argc != 2 && argc != 3)
07320       return CLI_SHOWUSAGE;
07321 
07322    if (argc == 3) { /* specific queue */
07323       if ((q = load_realtime_queue(argv[2]))) {
07324          queue_t_unref(q, "Done with temporary pointer");
07325       }
07326    } else if (ast_check_realtime("queues")) {
07327       /* This block is to find any queues which are defined in realtime but
07328        * which have not yet been added to the in-core container
07329        */
07330       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
07331       char *queuename;
07332       if (cfg) {
07333          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
07334             if ((q = load_realtime_queue(queuename))) {
07335                queue_t_unref(q, "Done with temporary pointer");
07336             }
07337          }
07338          ast_config_destroy(cfg);
07339       }
07340    }
07341 
07342    ao2_lock(queues);
07343    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
07344    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07345       float sl;
07346       struct call_queue *realtime_queue = NULL;
07347 
07348       ao2_lock(q);
07349       /* This check is to make sure we don't print information for realtime
07350        * queues which have been deleted from realtime but which have not yet
07351        * been deleted from the in-core container. Only do this if we're not
07352        * looking for a specific queue.
07353        */
07354       if (argc < 3 && q->realtime) {
07355          realtime_queue = load_realtime_queue(q->name);
07356          if (!realtime_queue) {
07357             ao2_unlock(q);
07358             queue_t_unref(q, "Done with iterator");
07359             continue;
07360          }
07361          queue_t_unref(realtime_queue, "Queue is already in memory");
07362       }
07363 
07364       if (argc == 3 && strcasecmp(q->name, argv[2])) {
07365          ao2_unlock(q);
07366          queue_t_unref(q, "Done with iterator");
07367          continue;
07368       }
07369       found = 1;
07370 
07371       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
07372       if (q->maxlen)
07373          ast_str_append(&out, 0, "%d", q->maxlen);
07374       else
07375          ast_str_append(&out, 0, "unlimited");
07376       sl = 0;
07377       if (q->callscompleted > 0)
07378          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
07379       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
07380          int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
07381          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
07382       do_print(s, fd, ast_str_buffer(out));
07383       if (!ao2_container_count(q->members))
07384          do_print(s, fd, "   No Members");
07385       else {
07386          struct member *mem;
07387 
07388          do_print(s, fd, "   Members: ");
07389          mem_iter = ao2_iterator_init(q->members, 0);
07390          while ((mem = ao2_iterator_next(&mem_iter))) {
07391             ast_str_set(&out, 0, "      %s", mem->membername);
07392             if (strcasecmp(mem->membername, mem->interface)) {
07393                ast_str_append(&out, 0, " (%s)", mem->interface);
07394             }
07395             if (mem->penalty)
07396                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
07397             ast_str_append(&out, 0, "%s%s%s (%s)",
07398                mem->dynamic ? " (dynamic)" : "",
07399                mem->realtime ? " (realtime)" : "",
07400                mem->paused ? " (paused)" : "",
07401                ast_devstate2str(mem->status));
07402             if (mem->calls)
07403                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
07404                   mem->calls, (long) (time(NULL) - mem->lastcall));
07405             else
07406                ast_str_append(&out, 0, " has taken no calls yet");
07407             do_print(s, fd, ast_str_buffer(out));
07408             ao2_ref(mem, -1);
07409          }
07410          ao2_iterator_destroy(&mem_iter);
07411       }
07412       if (!q->head)
07413          do_print(s, fd, "   No Callers");
07414       else {
07415          struct queue_ent *qe;
07416          int pos = 1;
07417 
07418          do_print(s, fd, "   Callers: ");
07419          for (qe = q->head; qe; qe = qe->next) {
07420             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
07421                pos++, qe->chan->name, (long) (now - qe->start) / 60,
07422                (long) (now - qe->start) % 60, qe->prio);
07423             do_print(s, fd, ast_str_buffer(out));
07424          }
07425       }
07426       do_print(s, fd, ""); /* blank line between entries */
07427       ao2_unlock(q);
07428       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
07429    }
07430    ao2_iterator_destroy(&queue_iter);
07431    ao2_unlock(queues);
07432    if (!found) {
07433       if (argc == 3)
07434          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07435       else
07436          ast_str_set(&out, 0, "No queues.");
07437       do_print(s, fd, ast_str_buffer(out));
07438    }
07439    return CLI_SUCCESS;
07440 }

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

05612 {
05613    struct call_queue *q;
05614    struct member *new_member, *old_member;
05615    int res = RES_NOSUCHQUEUE;
05616 
05617    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05618     * short-circuits if the queue is already in memory. */
05619    if (!(q = load_realtime_queue(queuename)))
05620       return res;
05621 
05622    ao2_lock(q);
05623    if ((old_member = interface_exists(q, interface)) == NULL) {
05624       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05625          new_member->dynamic = 1;
05626          member_add_to_queue(q, new_member);
05627          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05628             "Queue: %s\r\n"
05629             "Location: %s\r\n"
05630             "MemberName: %s\r\n"
05631             "Membership: %s\r\n"
05632             "Penalty: %d\r\n"
05633             "CallsTaken: %d\r\n"
05634             "LastCall: %d\r\n"
05635             "Status: %d\r\n"
05636             "Paused: %d\r\n",
05637             q->name, new_member->interface, new_member->membername,
05638             "dynamic",
05639             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05640             new_member->status, new_member->paused);
05641          
05642          ao2_ref(new_member, -1);
05643          new_member = NULL;
05644 
05645          if (dump)
05646             dump_queue_members(q);
05647          
05648          res = RES_OKAY;
05649       } else {
05650          res = RES_OUTOFMEMORY;
05651       }
05652    } else {
05653       ao2_ref(old_member, -1);
05654       res = RES_EXISTS;
05655    }
05656    ao2_unlock(q);
05657    queue_t_unref(q, "Expiring temporary reference");
05658 
05659    return res;
05660 }

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

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

02320 {
02321    struct call_queue *q;
02322 
02323    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02324       if (ast_string_field_init(q, 64)) {
02325          queue_t_unref(q, "String field allocation failed");
02326          return NULL;
02327       }
02328       ast_string_field_set(q, name, queuename);
02329    }
02330    return q;
02331 }

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

AddQueueMember application.

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

06044 {
06045    int res=-1;
06046    char *parse, *temppos = NULL;
06047    AST_DECLARE_APP_ARGS(args,
06048       AST_APP_ARG(queuename);
06049       AST_APP_ARG(interface);
06050       AST_APP_ARG(penalty);
06051       AST_APP_ARG(options);
06052       AST_APP_ARG(membername);
06053       AST_APP_ARG(state_interface);
06054    );
06055    int penalty = 0;
06056 
06057    if (ast_strlen_zero(data)) {
06058       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
06059       return -1;
06060    }
06061 
06062    parse = ast_strdupa(data);
06063 
06064    AST_STANDARD_APP_ARGS(args, parse);
06065 
06066    if (ast_strlen_zero(args.interface)) {
06067       args.interface = ast_strdupa(chan->name);
06068       temppos = strrchr(args.interface, '-');
06069       if (temppos)
06070          *temppos = '\0';
06071    }
06072 
06073    if (!ast_strlen_zero(args.penalty)) {
06074       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
06075          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
06076          penalty = 0;
06077       }
06078    }
06079 
06080    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
06081    case RES_OKAY:
06082       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
06083       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
06084       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
06085       res = 0;
06086       break;
06087    case RES_EXISTS:
06088       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
06089       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
06090       res = 0;
06091       break;
06092    case RES_NOSUCHQUEUE:
06093       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
06094       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
06095       res = 0;
06096       break;
06097    case RES_OUTOFMEMORY:
06098       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
06099       break;
06100    }
06101 
06102    return res;
06103 }

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

References ast_channel_datastore_find().

Referenced by try_calling().

04591 {
04592    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04593 }

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

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

01284 {
01285    int x;
01286    /*This 'double check' that default value is OFF */
01287    if (ast_strlen_zero(autopause))
01288       return QUEUE_AUTOPAUSE_OFF;
01289 
01290    /*This 'double check' is to ensure old values works */
01291    if(ast_true(autopause))
01292       return QUEUE_AUTOPAUSE_ON;
01293 
01294    for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
01295       if (!strcasecmp(autopause, autopausesmodes[x].name))
01296          return autopausesmodes[x].autopause;
01297    }
01298 
01299    /*This 'double check' that default value is OFF */
01300    return QUEUE_AUTOPAUSE_OFF;
01301 }

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

04416 {
04417    /* disregarding penalty on too few members? */
04418    int membercount = ao2_container_count(q->members);
04419    unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04420 
04421    if (usepenalty) {
04422       if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) ||
04423          (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) {
04424          return -1;
04425       }
04426    } else {
04427       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04428            membercount, q->penaltymemberslimit);
04429    }
04430 
04431    switch (q->strategy) {
04432    case QUEUE_STRATEGY_RINGALL:
04433       /* Everyone equal, except for penalty */
04434       tmp->metric = mem->penalty * 1000000 * usepenalty;
04435       break;
04436    case QUEUE_STRATEGY_LINEAR:
04437       if (pos < qe->linpos) {
04438          tmp->metric = 1000 + pos;
04439       } else {
04440          if (pos > qe->linpos)
04441             /* Indicate there is another priority */
04442             qe->linwrapped = 1;
04443          tmp->metric = pos;
04444       }
04445       tmp->metric += mem->penalty * 1000000 * usepenalty;
04446       break;
04447    case QUEUE_STRATEGY_RRORDERED:
04448    case QUEUE_STRATEGY_RRMEMORY:
04449       pos = mem->queuepos;
04450       if (pos < q->rrpos) {
04451          tmp->metric = 1000 + pos;
04452       } else {
04453          if (pos > q->rrpos)
04454             /* Indicate there is another priority */
04455             q->wrapped = 1;
04456          tmp->metric = pos;
04457       }
04458       tmp->metric += mem->penalty * 1000000 * usepenalty;
04459       break;
04460    case QUEUE_STRATEGY_RANDOM:
04461       tmp->metric = ast_random() % 1000;
04462       tmp->metric += mem->penalty * 1000000 * usepenalty;
04463       break;
04464    case QUEUE_STRATEGY_WRANDOM:
04465       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04466       break;
04467    case QUEUE_STRATEGY_FEWESTCALLS:
04468       tmp->metric = mem->calls;
04469       tmp->metric += mem->penalty * 1000000 * usepenalty;
04470       break;
04471    case QUEUE_STRATEGY_LEASTRECENT:
04472       if (!mem->lastcall)
04473          tmp->metric = 0;
04474       else
04475          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04476       tmp->metric += mem->penalty * 1000000 * usepenalty;
04477       break;
04478    default:
04479       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04480       break;
04481    }
04482    return 0;
04483 }

static void callattempt_free ( struct callattempt doomed  )  [static]

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

02982 {
02983    if (doomed->member) {
02984       ao2_ref(doomed->member, -1);
02985    }
02986    ast_party_connected_line_free(&doomed->connected);
02987    ast_free(doomed);
02988 }

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

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

03199 {
03200    if (call->member->paused) {
03201       ast_debug(1, "%s paused, can't receive call\n", call->interface);
03202       return 0;
03203    }
03204 
03205    if (!qe->parent->ringinuse && !member_status_available(call->member->status)) {
03206       ast_debug(1, "%s not available, can't receive call\n", call->interface);
03207       return 0;
03208    }
03209 
03210    if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime))
03211       || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) {
03212       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
03213          (call->lastqueue ? call->lastqueue->name : qe->parent->name),
03214          call->interface);
03215       return 0;
03216    }
03217 
03218    if (use_weight && compare_weight(qe->parent, call->member)) {
03219       ast_debug(1, "Priority queue delaying call to %s:%s\n",
03220          qe->parent->name, call->interface);
03221       return 0;
03222    }
03223 
03224    if (!qe->parent->ringinuse) {
03225       if (member_call_pending_set(call->member)) {
03226          ast_debug(1, "%s has another call pending, can't receive call\n",
03227             call->interface);
03228          return 0;
03229       }
03230 
03231       /*
03232        * The queue member is available.  Get current status to be sure
03233        * because the device state and extension state callbacks may
03234        * not have updated the status yet.
03235        */
03236       if (!member_status_available(get_queue_member_status(call->member))) {
03237          ast_debug(1, "%s actually not available, can't receive call\n",
03238             call->interface);
03239          member_call_pending_clear(call->member);
03240          return 0;
03241       }
03242    }
03243 
03244    return 1;
03245 }

static void clear_queue ( struct call_queue q  )  [static]

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

01849 {
01850    q->holdtime = 0;
01851    q->callscompleted = 0;
01852    q->callsabandoned = 0;
01853    q->callscompletedinsl = 0;
01854    q->talktime = 0;
01855 
01856    if (q->members) {
01857       struct member *mem;
01858       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01859       while ((mem = ao2_iterator_next(&mem_iter))) {
01860          mem->calls = 0;
01861          mem->lastcall = 0;
01862          ao2_ref(mem, -1);
01863       }
01864       ao2_iterator_destroy(&mem_iter);
01865    }
01866 }

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

07250 {
07251    struct call_queue *q;
07252    struct ao2_iterator queue_iter;
07253 
07254    queue_iter = ao2_iterator_init(queues, 0);
07255    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07256       ao2_lock(q);
07257       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
07258          clear_queue(q);
07259       ao2_unlock(q);
07260       queue_t_unref(q, "Done with iterator");
07261    }
07262    ao2_iterator_destroy(&queue_iter);
07263    return 0;
07264 }

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

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

03062 {
03063    struct call_queue *q;
03064    struct member *mem;
03065    int found = 0;
03066    struct ao2_iterator queue_iter;
03067 
03068    queue_iter = ao2_iterator_init(queues, 0);
03069    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03070       if (q == rq) { /* don't check myself, could deadlock */
03071          queue_t_unref(q, "Done with iterator");
03072          continue;
03073       }
03074       ao2_lock(q);
03075       if (q->count && q->members) {
03076          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
03077             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
03078             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
03079                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);
03080                found = 1;
03081             }
03082             ao2_ref(mem, -1);
03083          }
03084       }
03085       ao2_unlock(q);
03086       queue_t_unref(q, "Done with iterator");
03087       if (found) {
03088          break;
03089       }
03090    }
03091    ao2_iterator_destroy(&queue_iter);
03092    return found;
03093 }

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

07515 {
07516    struct call_queue *q;
07517    char *ret = NULL;
07518    int which = 0;
07519    int wordlen = strlen(word);
07520    struct ao2_iterator queue_iter;
07521    const char *word_list = NULL;
07522 
07523    /* for certain commands, already completed items should be left out of
07524     * the list */
07525    if (word_list_offset && strlen(line) >= word_list_offset) {
07526       word_list = line + word_list_offset;
07527    }
07528 
07529    queue_iter = ao2_iterator_init(queues, 0);
07530    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07531       if (!strncasecmp(word, q->name, wordlen) && ++which > state
07532          && (!word_list_offset || !word_in_list(word_list, q->name))) {
07533          ret = ast_strdup(q->name);
07534          queue_t_unref(q, "Done with iterator");
07535          break;
07536       }
07537       queue_t_unref(q, "Done with iterator");
07538    }
07539    ao2_iterator_destroy(&queue_iter);
07540 
07541    /* Pretend "rules" is at the end of the queues list in certain
07542     * circumstances since it is an alternate command that should be
07543     * tab-completable for "queue show" */
07544    if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) {
07545       ret = ast_strdup("rules");
07546    }
07547 
07548    return ret;
07549 }

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

Definition at line 7976 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07977 {
07978    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07979    switch (pos) {
07980    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07981       return NULL;
07982    case 4: /* only one possible match, "to" */
07983       return state == 0 ? ast_strdup("to") : NULL;
07984    case 5: /* <queue> */
07985       return complete_queue(line, word, pos, state, 0);
07986    case 6: /* only one possible match, "penalty" */
07987       return state == 0 ? ast_strdup("penalty") : NULL;
07988    case 7:
07989       if (state < 100) {      /* 0-99 */
07990          char *num;
07991          if ((num = ast_malloc(3))) {
07992             sprintf(num, "%d", state);
07993          }
07994          return num;
07995       } else {
07996          return NULL;
07997       }
07998    case 8: /* only one possible match, "as" */
07999       return state == 0 ? ast_strdup("as") : NULL;
08000    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
08001       return NULL;
08002    default:
08003       return NULL;
08004    }
08005 }

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

Definition at line 8198 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

08199 {
08200    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
08201    switch (pos) {
08202    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
08203       return NULL;
08204    case 4:  /* only one possible match, "queue" */
08205       return state == 0 ? ast_strdup("queue") : NULL;
08206    case 5:  /* <queue> */
08207       return complete_queue(line, word, pos, state, 0);
08208    case 6: /* "reason" */
08209       return state == 0 ? ast_strdup("reason") : NULL;
08210    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
08211       return NULL;
08212    default:
08213       return NULL;
08214    }
08215 }

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

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

08107 {
08108    int which = 0;
08109    struct call_queue *q;
08110    struct member *m;
08111    struct ao2_iterator queue_iter;
08112    struct ao2_iterator mem_iter;
08113    int wordlen = strlen(word);
08114 
08115    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
08116    if (pos > 5 || pos < 3)
08117       return NULL;
08118    if (pos == 4)   /* only one possible match, 'from' */
08119       return (state == 0 ? ast_strdup("from") : NULL);
08120 
08121    if (pos == 5) {  /* No need to duplicate code */
08122       return complete_queue(line, word, pos, state, 0);
08123    }
08124 
08125    /* here is the case for 3, <member> */
08126    queue_iter = ao2_iterator_init(queues, 0);
08127    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
08128       ao2_lock(q);
08129       mem_iter = ao2_iterator_init(q->members, 0);
08130       while ((m = ao2_iterator_next(&mem_iter))) {
08131          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
08132             char *tmp;
08133             tmp = ast_strdup(m->interface);
08134             ao2_ref(m, -1);
08135             ao2_iterator_destroy(&mem_iter);
08136             ao2_unlock(q);
08137             queue_t_unref(q, "Done with iterator, returning interface name");
08138             ao2_iterator_destroy(&queue_iter);
08139             return tmp;
08140          }
08141          ao2_ref(m, -1);
08142       }
08143       ao2_iterator_destroy(&mem_iter);
08144       ao2_unlock(q);
08145       queue_t_unref(q, "Done with iterator");
08146    }
08147    ao2_iterator_destroy(&queue_iter);
08148 
08149    return NULL;
08150 }

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

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

08332 {
08333    int which = 0;
08334    struct rule_list *rl_iter;
08335    int wordlen = strlen(word);
08336    char *ret = NULL;
08337    if (pos != 3) /* Wha? */ {
08338       return NULL;
08339    }
08340 
08341    AST_LIST_LOCK(&rule_lists);
08342    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08343       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
08344          ret = ast_strdup(rl_iter->name);
08345          break;
08346       }
08347    }
08348    AST_LIST_UNLOCK(&rule_lists);
08349 
08350    return ret;
08351 }

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

Definition at line 8268 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

08269 {
08270    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
08271    switch (pos) {
08272    case 4:
08273       if (state == 0) {
08274          return ast_strdup("on");
08275       } else {
08276          return NULL;
08277       }
08278    case 6:
08279       if (state == 0) {
08280          return ast_strdup("in");
08281       } else {
08282          return NULL;
08283       }
08284    case 7:
08285       return complete_queue(line, word, pos, state, 0);
08286    default:
08287       return NULL;
08288    }
08289 }

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

Definition at line 7551 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07552 {
07553    if (pos == 2) {
07554       return complete_queue(line, word, pos, state, 0);
07555    }
07556    return NULL;
07557 }

static int compress_char ( const char  c  )  [static]

Definition at line 1739 of file app_queue.c.

Referenced by member_hash_fn().

01740 {
01741    if (c < 32)
01742       return 0;
01743    else if (c > 96)
01744       return c - 64;
01745    else
01746       return c - 32;
01747 }

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

Copy rule from global list into specified queue.

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

06141 {
06142    struct penalty_rule *pr_iter;
06143    struct rule_list *rl_iter;
06144    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
06145    AST_LIST_LOCK(&rule_lists);
06146    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06147       if (!strcasecmp(rl_iter->name, tmp))
06148          break;
06149    }
06150    if (rl_iter) {
06151       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06152          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
06153          if (!new_pr) {
06154             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
06155             break;
06156          }
06157          new_pr->time = pr_iter->time;
06158          new_pr->max_value = pr_iter->max_value;
06159          new_pr->min_value = pr_iter->min_value;
06160          new_pr->max_relative = pr_iter->max_relative;
06161          new_pr->min_relative = pr_iter->min_relative;
06162          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
06163       }
06164    }
06165    AST_LIST_UNLOCK(&rule_lists);
06166 }

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

01708 {
01709    struct member *cur;
01710    
01711    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01712       cur->penalty = penalty;
01713       cur->paused = paused;
01714       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01715       if (!ast_strlen_zero(state_interface))
01716          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01717       else
01718          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01719       if (!ast_strlen_zero(membername))
01720          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01721       else
01722          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01723       if (!strchr(cur->interface, '/'))
01724          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01725       if (!strncmp(cur->state_interface, "hint:", 5)) {
01726          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01727          char *exten = strsep(&context, "@") + 5;
01728 
01729          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01730          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01731       }
01732       cur->status = get_queue_member_status(cur);
01733    }
01734 
01735    return cur;
01736 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

02306 {
02307    struct call_queue *q = obj;
02308    int i;
02309 
02310    free_members(q, 1);
02311    ast_string_field_free_memory(q);
02312    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02313       if (q->sound_periodicannounce[i])
02314          free(q->sound_periodicannounce[i]);
02315    }
02316    ao2_ref(q->members, -1);
02317 }

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

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

01606 {
01607    enum ast_device_state state;
01608    const char *device;
01609    struct statechange *sc;
01610    size_t datapsize;
01611 
01612    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01613    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01614 
01615    if (ast_strlen_zero(device)) {
01616       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01617       return;
01618    }
01619    datapsize = sizeof(*sc) + strlen(device) + 1;
01620    if (!(sc = ast_calloc(1, datapsize))) {
01621       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01622       return;
01623    }
01624    sc->state = state;
01625    strcpy(sc->dev, device);
01626    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01627       ast_free(sc);
01628    }
01629 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 3096 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

03097 {
03098    o->stillgoing = 0;
03099    ast_hangup(o->chan);
03100    o->chan = NULL;
03101 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

07297 {
07298    if (s)
07299       astman_append(s, "%s\r\n", str);
07300    else
07301       ast_cli(fd, "%s\n", str);
07302 }

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

05512 {
05513    struct member *cur_member;
05514    struct ast_str *value;
05515    struct ao2_iterator mem_iter;
05516 
05517    if (!pm_queue) {
05518       return;
05519    }
05520 
05521    /* 4K is a reasonable default for most applications, but we grow to
05522     * accommodate more if necessary. */
05523    if (!(value = ast_str_create(4096))) {
05524       return;
05525    }
05526 
05527    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05528    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05529       if (!cur_member->dynamic) {
05530          ao2_ref(cur_member, -1);
05531          continue;
05532       }
05533 
05534       ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s",
05535          ast_str_strlen(value) ? "|" : "",
05536          cur_member->interface,
05537          cur_member->penalty,
05538          cur_member->paused,
05539          cur_member->membername,
05540          cur_member->state_interface);
05541 
05542       ao2_ref(cur_member, -1);
05543    }
05544    ao2_iterator_destroy(&mem_iter);
05545 
05546    if (ast_str_strlen(value) && !cur_member) {
05547       if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value)))
05548          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05549    } else {
05550       /* Delete the entry if the queue is empty or there is an error */
05551       ast_db_del(pm_family, pm_queue->name);
05552    }
05553 
05554    ast_free(value);
05555 }

static void end_bridge_callback ( void *  data  )  [static]

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

04639 {
04640    struct queue_end_bridge *qeb = data;
04641    struct call_queue *q = qeb->q;
04642    struct ast_channel *chan = qeb->chan;
04643 
04644    if (ao2_ref(qeb, -1) == 1) {
04645       set_queue_variables(q, chan);
04646       /* This unrefs the reference we made in try_calling when we allocated qeb */
04647       queue_t_unref(q, "Expire bridge_config reference");
04648    }
04649 }

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

Definition at line 4631 of file app_queue.c.

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

Referenced by try_calling().

04632 {
04633    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04634    ao2_ref(qeb, +1);
04635    qeb->chan = originator;
04636 }

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

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

01664 {
01665    struct ao2_iterator miter, qiter;
01666    struct member *m;
01667    struct call_queue *q;
01668    int found = 0, device_state = extensionstate2devicestate(state);
01669 
01670    qiter = ao2_iterator_init(queues, 0);
01671    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01672       ao2_lock(q);
01673 
01674       miter = ao2_iterator_init(q->members, 0);
01675       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01676          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01677             update_status(q, m, device_state);
01678             ao2_ref(m, -1);
01679             found = 1;
01680             break;
01681          }
01682       }
01683       ao2_iterator_destroy(&miter);
01684 
01685       ao2_unlock(q);
01686       queue_t_unref(q, "Done with iterator");
01687    }
01688    ao2_iterator_destroy(&qiter);
01689 
01690         if (found) {
01691       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01692    } else {
01693       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",
01694            exten, context, device_state, ast_devstate2str(device_state));
01695    }
01696 
01697    return 0;
01698 }

static int extensionstate2devicestate ( int  state  )  [static]

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

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

01633 {
01634    switch (state) {
01635    case AST_EXTENSION_NOT_INUSE:
01636       state = AST_DEVICE_NOT_INUSE;
01637       break;
01638    case AST_EXTENSION_INUSE:
01639       state = AST_DEVICE_INUSE;
01640       break;
01641    case AST_EXTENSION_BUSY:
01642       state = AST_DEVICE_BUSY;
01643       break;
01644    case AST_EXTENSION_RINGING:
01645       state = AST_DEVICE_RINGING;
01646       break;
01647    case AST_EXTENSION_ONHOLD:
01648       state = AST_DEVICE_ONHOLD;
01649       break;
01650    case AST_EXTENSION_UNAVAILABLE:
01651       state = AST_DEVICE_UNAVAILABLE;
01652       break;
01653    case AST_EXTENSION_REMOVED:
01654    case AST_EXTENSION_DEACTIVATED:
01655    default:
01656       state = AST_DEVICE_INVALID;
01657       break;
01658    }
01659 
01660    return state;
01661 }

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

find the entry with the best metric, or NULL

Definition at line 3419 of file app_queue.c.

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

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

03420 {
03421    struct callattempt *best = NULL, *cur;
03422 
03423    for (cur = outgoing; cur; cur = cur->q_next) {
03424       if (cur->stillgoing &&              /* Not already done */
03425          !cur->chan &&              /* Isn't already going */
03426          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03427          best = cur;
03428       }
03429    }
03430 
03431    return best;
03432 }

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

02344 {
02345    struct ast_variable *v;
02346    struct call_queue *q, tmpq = {
02347       .name = queuename,   
02348    };
02349    struct member *m;
02350    struct ao2_iterator mem_iter;
02351    char *interface = NULL;
02352    const char *tmp_name;
02353    char *tmp;
02354    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
02355 
02356    /* Static queues override realtime. */
02357    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02358       ao2_lock(q);
02359       if (!q->realtime) {
02360          if (q->dead) {
02361             ao2_unlock(q);
02362             queue_t_unref(q, "Queue is dead; can't return it");
02363             return NULL;
02364          } else {
02365             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02366             ao2_unlock(q);
02367             return q;
02368          }
02369       }
02370    } else if (!member_config)
02371       /* Not found in the list, and it's not realtime ... */
02372       return NULL;
02373 
02374    /* Check if queue is defined in realtime. */
02375    if (!queue_vars) {
02376       /* Delete queue from in-core list if it has been deleted in realtime. */
02377       if (q) {
02378          /*! \note Hmm, can't seem to distinguish a DB failure from a not
02379             found condition... So we might delete an in-core queue
02380             in case of DB failure. */
02381          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02382 
02383          q->dead = 1;
02384          /* Delete if unused (else will be deleted when last caller leaves). */
02385          queues_t_unlink(queues, q, "Unused; removing from container");
02386          ao2_unlock(q);
02387          queue_t_unref(q, "Queue is dead; can't return it");
02388       }
02389       return NULL;
02390    }
02391 
02392    /* Create a new queue if an in-core entry does not exist yet. */
02393    if (!q) {
02394       struct ast_variable *tmpvar = NULL;
02395       if (!(q = alloc_queue(queuename)))
02396          return NULL;
02397       ao2_lock(q);
02398       clear_queue(q);
02399       q->realtime = 1;
02400       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
02401        * will allocate the members properly
02402        */
02403       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02404          if (!strcasecmp(tmpvar->name, "strategy")) {
02405             q->strategy = strat2int(tmpvar->value);
02406             if (q->strategy < 0) {
02407                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02408                tmpvar->value, q->name);
02409                q->strategy = QUEUE_STRATEGY_RINGALL;
02410             }
02411             break;
02412          }
02413       }
02414       /* We traversed all variables and didn't find a strategy */
02415       if (!tmpvar)
02416          q->strategy = QUEUE_STRATEGY_RINGALL;
02417       queues_t_link(queues, q, "Add queue to container");
02418    }
02419    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
02420 
02421    memset(tmpbuf, 0, sizeof(tmpbuf));
02422    for (v = queue_vars; v; v = v->next) {
02423       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
02424       if (strchr(v->name, '_')) {
02425          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02426          tmp_name = tmpbuf;
02427          tmp = tmpbuf;
02428          while ((tmp = strchr(tmp, '_')))
02429             *tmp++ = '-';
02430       } else
02431          tmp_name = v->name;
02432 
02433       /* NULL values don't get returned from realtime; blank values should
02434        * still get set.  If someone doesn't want a value to be set, they
02435        * should set the realtime column to NULL, not blank. */
02436       queue_set_param(q, tmp_name, v->value, -1, 0);
02437    }
02438 
02439    /* Temporarily set realtime members dead so we can detect deleted ones. */
02440    mem_iter = ao2_iterator_init(q->members, 0);
02441    while ((m = ao2_iterator_next(&mem_iter))) {
02442       if (m->realtime)
02443          m->dead = 1;
02444       ao2_ref(m, -1);
02445    }
02446    ao2_iterator_destroy(&mem_iter);
02447 
02448    while ((interface = ast_category_browse(member_config, interface))) {
02449       rt_handle_member_record(q, interface,
02450          ast_variable_retrieve(member_config, interface, "uniqueid"),
02451          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02452          ast_variable_retrieve(member_config, interface, "penalty"),
02453          ast_variable_retrieve(member_config, interface, "paused"),
02454          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02455    }
02456 
02457    /* Delete all realtime members that have been deleted in DB. */
02458    mem_iter = ao2_iterator_init(q->members, 0);
02459    while ((m = ao2_iterator_next(&mem_iter))) {
02460       if (m->dead) {
02461          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02462          member_remove_from_queue(q, m);
02463       }
02464       ao2_ref(m, -1);
02465    }
02466    ao2_iterator_destroy(&mem_iter);
02467 
02468    ao2_unlock(q);
02469 
02470    return q;
02471 }

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

Iterate through queue's member list and delete them.

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

02290 {
02291    /* Free non-dynamic members */
02292    struct member *cur;
02293    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02294 
02295    while ((cur = ao2_iterator_next(&mem_iter))) {
02296       if (all || !cur->dynamic) {
02297          member_remove_from_queue(q, cur);
02298       }
02299       ao2_ref(cur, -1);
02300    }
02301    ao2_iterator_destroy(&mem_iter);
02302 }

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

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

05789 {
05790    int foundqueue = 0, penalty;
05791    struct call_queue *q, tmpq = {
05792       .name = queuename,   
05793    };
05794    struct member *mem;
05795    
05796    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05797       foundqueue = 1;
05798       ao2_lock(q);
05799       if ((mem = interface_exists(q, interface))) {
05800          penalty = mem->penalty;
05801          ao2_ref(mem, -1);
05802          ao2_unlock(q);
05803          queue_t_unref(q, "Search complete");
05804          return penalty;
05805       }
05806       ao2_unlock(q);
05807       queue_t_unref(q, "Search complete");
05808    }
05809 
05810    /* some useful debuging */
05811    if (foundqueue) 
05812       ast_log (LOG_ERROR, "Invalid queuename\n");
05813    else 
05814       ast_log (LOG_ERROR, "Invalid interface\n");
05815 
05816    return RESULT_FAILURE;
05817 }

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

Check if members are available.

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

Definition at line 1451 of file app_queue.c.

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

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

01452 {
01453    struct member *member;
01454    struct ao2_iterator mem_iter;
01455 
01456    ao2_lock(q);
01457    mem_iter = ao2_iterator_init(q->members, 0);
01458    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01459       if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) {
01460          if (conditions & QUEUE_EMPTY_PENALTY) {
01461             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01462             continue;
01463          }
01464       }
01465 
01466       switch (devstate ? ast_device_state(member->state_interface) : member->status) {
01467       case AST_DEVICE_INVALID:
01468          if (conditions & QUEUE_EMPTY_INVALID) {
01469             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01470             break;
01471          }
01472          goto default_case;
01473       case AST_DEVICE_UNAVAILABLE:
01474          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01475             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01476             break;
01477          }
01478          goto default_case;
01479       case AST_DEVICE_INUSE:
01480          if (conditions & QUEUE_EMPTY_INUSE) {
01481             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01482             break;
01483          }
01484          goto default_case;
01485       case AST_DEVICE_RINGING:
01486          if (conditions & QUEUE_EMPTY_RINGING) {
01487             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01488             break;
01489          }
01490          goto default_case;
01491       case AST_DEVICE_UNKNOWN:
01492          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01493             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01494             break;
01495          }
01496          /* Fall-through */
01497       default:
01498       default_case:
01499          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01500             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01501             break;
01502          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01503             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);
01504             break;
01505          } else {
01506             ao2_ref(member, -1);
01507             ao2_iterator_destroy(&mem_iter);
01508             ao2_unlock(q);
01509             ast_debug(4, "%s is available.\n", member->membername);
01510             return 0;
01511          }
01512          break;
01513       }
01514    }
01515    ao2_iterator_destroy(&mem_iter);
01516    ao2_unlock(q);
01517 
01518    if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) {
01519       /* member state still may be RINGING due to lag in event message - check again with device state */
01520       return get_member_status(q, max_penalty, min_penalty, conditions, 1);
01521    }
01522    return -1;
01523 }

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

08033 {
08034    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
08035    int penalty;
08036 
08037    switch ( cmd ) {
08038    case CLI_INIT:
08039       e->command = "queue add member";
08040       e->usage =
08041          "Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
08042          "       Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally:  a penalty, membername and a state_interface\n";
08043       return NULL;
08044    case CLI_GENERATE:
08045       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
08046    }
08047 
08048    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
08049       return CLI_SHOWUSAGE;
08050    } else if (strcmp(a->argv[4], "to")) {
08051       return CLI_SHOWUSAGE;
08052    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
08053       return CLI_SHOWUSAGE;
08054    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
08055       return CLI_SHOWUSAGE;
08056    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
08057       return CLI_SHOWUSAGE;
08058    }
08059 
08060    queuename = a->argv[5];
08061    interface = a->argv[3];
08062    if (a->argc >= 8) {
08063       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
08064          if (penalty < 0) {
08065             ast_cli(a->fd, "Penalty must be >= 0\n");
08066             penalty = 0;
08067          }
08068       } else {
08069          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
08070          penalty = 0;
08071       }
08072    } else {
08073       penalty = 0;
08074    }
08075 
08076    if (a->argc >= 10) {
08077       membername = a->argv[9];
08078    }
08079 
08080    if (a->argc >= 12) {
08081       state_interface = a->argv[11];
08082    }
08083 
08084    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
08085    case RES_OKAY:
08086       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
08087       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
08088       return CLI_SUCCESS;
08089    case RES_EXISTS:
08090       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
08091       return CLI_FAILURE;
08092    case RES_NOSUCHQUEUE:
08093       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
08094       return CLI_FAILURE;
08095    case RES_OUTOFMEMORY:
08096       ast_cli(a->fd, "Out of memory\n");
08097       return CLI_FAILURE;
08098    case RES_NOT_DYNAMIC:
08099       ast_cli(a->fd, "Member not dynamic\n");
08100       return CLI_FAILURE;
08101    default:
08102       return CLI_FAILURE;
08103    }
08104 }

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

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

08218 {
08219    const char *queuename, *interface, *reason;
08220    int paused;
08221 
08222    switch (cmd) {
08223    case CLI_INIT:
08224       e->command = "queue {pause|unpause} member";
08225       e->usage = 
08226          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
08227          "  Pause or unpause a queue member. Not specifying a particular queue\n"
08228          "  will pause or unpause a member across all queues to which the member\n"
08229          "  belongs.\n";
08230       return NULL;
08231    case CLI_GENERATE:
08232       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
08233    }
08234 
08235    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
08236       return CLI_SHOWUSAGE;
08237    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
08238       return CLI_SHOWUSAGE;
08239    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
08240       return CLI_SHOWUSAGE;
08241    }
08242 
08243 
08244    interface = a->argv[3];
08245    queuename = a->argc >= 6 ? a->argv[5] : NULL;
08246    reason = a->argc == 8 ? a->argv[7] : NULL;
08247    paused = !strcasecmp(a->argv[1], "pause");
08248 
08249    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
08250       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
08251       if (!ast_strlen_zero(queuename))
08252          ast_cli(a->fd, " in queue '%s'", queuename);
08253       if (!ast_strlen_zero(reason))
08254          ast_cli(a->fd, " for reason '%s'", reason);
08255       ast_cli(a->fd, "\n");
08256       return CLI_SUCCESS;
08257    } else {
08258       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
08259       if (!ast_strlen_zero(queuename))
08260          ast_cli(a->fd, " in queue '%s'", queuename);
08261       if (!ast_strlen_zero(reason))
08262          ast_cli(a->fd, " for reason '%s'", reason);
08263       ast_cli(a->fd, "\n");
08264       return CLI_FAILURE;
08265    }
08266 }

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

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

08427 {
08428    struct ast_flags mask = {0,};
08429    int i;
08430 
08431    switch (cmd) {
08432       case CLI_INIT:
08433          e->command = "queue reload {parameters|members|rules|all}";
08434          e->usage =
08435             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08436             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08437             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08438             "specified in order to know what information to reload. Below is an explanation\n"
08439             "of each of these qualifiers.\n"
08440             "\n"
08441             "\t'members' - reload queue members from queues.conf\n"
08442             "\t'parameters' - reload all queue options except for queue members\n"
08443             "\t'rules' - reload the queuerules.conf file\n"
08444             "\t'all' - reload queue rules, parameters, and members\n"
08445             "\n"
08446             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08447             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08448             "one queue is specified when using this command, reloading queue rules may cause\n"
08449             "other queues to be affected\n";
08450          return NULL;
08451       case CLI_GENERATE:
08452          if (a->pos >= 3) {
08453             /* find the point at which the list of queue names starts */
08454             const char *command_end = a->line + strlen("queue reload ");
08455             command_end = strchr(command_end, ' ');
08456             if (!command_end) {
08457                command_end = a->line + strlen(a->line);
08458             }
08459             return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line);
08460          } else {
08461             return NULL;
08462          }
08463    }
08464 
08465    if (a->argc < 3)
08466       return CLI_SHOWUSAGE;
08467 
08468    if (!strcasecmp(a->argv[2], "rules")) {
08469       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08470    } else if (!strcasecmp(a->argv[2], "members")) {
08471       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08472    } else if (!strcasecmp(a->argv[2], "parameters")) {
08473       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08474    } else if (!strcasecmp(a->argv[2], "all")) {
08475       ast_set_flag(&mask, AST_FLAGS_ALL);
08476    }
08477 
08478    if (a->argc == 3) {
08479       reload_handler(1, &mask, NULL);
08480       return CLI_SUCCESS;
08481    }
08482 
08483    for (i = 3; i < a->argc; ++i) {
08484       reload_handler(1, &mask, a->argv[i]);
08485    }
08486 
08487    return CLI_SUCCESS;
08488 }

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

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

08153 {
08154    const char *queuename, *interface;
08155 
08156    switch (cmd) {
08157    case CLI_INIT:
08158       e->command = "queue remove member";
08159       e->usage = 
08160          "Usage: queue remove member <channel> from <queue>\n"
08161          "       Remove a specific channel from a queue.\n";
08162       return NULL;
08163    case CLI_GENERATE:
08164       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
08165    }
08166 
08167    if (a->argc != 6) {
08168       return CLI_SHOWUSAGE;
08169    } else if (strcmp(a->argv[4], "from")) {
08170       return CLI_SHOWUSAGE;
08171    }
08172 
08173    queuename = a->argv[5];
08174    interface = a->argv[3];
08175 
08176    switch (remove_from_queue(queuename, interface)) {
08177    case RES_OKAY:
08178       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
08179       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
08180       return CLI_SUCCESS;
08181    case RES_EXISTS:
08182       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
08183       return CLI_FAILURE;
08184    case RES_NOSUCHQUEUE:
08185       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
08186       return CLI_FAILURE;
08187    case RES_OUTOFMEMORY:
08188       ast_cli(a->fd, "Out of memory\n");
08189       return CLI_FAILURE;
08190    case RES_NOT_DYNAMIC:
08191       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
08192       return CLI_FAILURE;
08193    default:
08194       return CLI_FAILURE;
08195    }
08196 }

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

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

08388 {
08389    struct ast_flags mask = {QUEUE_RESET_STATS,};
08390    int i;
08391 
08392    switch (cmd) {
08393       case CLI_INIT:
08394          e->command = "queue reset stats";
08395          e->usage =
08396             "Usage: queue reset stats [<queuenames>]\n"
08397             "\n"
08398             "Issuing this command will reset statistics for\n"
08399             "<queuenames>, or for all queues if no queue is\n"
08400             "specified.\n";
08401          return NULL;
08402       case CLI_GENERATE:
08403          if (a->pos >= 3) {
08404             return complete_queue(a->line, a->word, a->pos, a->n, 17);
08405          } else {
08406             return NULL;
08407          }
08408    }
08409 
08410    if (a->argc < 3) {
08411       return CLI_SHOWUSAGE;
08412    }
08413 
08414    if (a->argc == 3) {
08415       reload_handler(1, &mask, NULL);
08416       return CLI_SUCCESS;
08417    }
08418 
08419    for (i = 3; i < a->argc; ++i) {
08420       reload_handler(1, &mask, a->argv[i]);
08421    }
08422 
08423    return CLI_SUCCESS;
08424 }

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

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

08354 {
08355    const char *rule;
08356    struct rule_list *rl_iter;
08357    struct penalty_rule *pr_iter;
08358    switch (cmd) {
08359    case CLI_INIT:
08360       e->command = "queue show rules";
08361       e->usage =
08362       "Usage: queue show rules [rulename]\n"
08363       "  Show the list of rules associated with rulename. If no\n"
08364       "  rulename is specified, list all rules defined in queuerules.conf\n";
08365       return NULL;
08366    case CLI_GENERATE:
08367       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
08368    }
08369 
08370    if (a->argc != 3 && a->argc != 4)
08371       return CLI_SHOWUSAGE;
08372 
08373    rule = a->argc == 4 ? a->argv[3] : "";
08374    AST_LIST_LOCK(&rule_lists);
08375    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08376       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
08377          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
08378          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
08379             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);
08380          }
08381       }
08382    }
08383    AST_LIST_UNLOCK(&rule_lists);
08384    return CLI_SUCCESS; 
08385 }

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

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

08292 {
08293    const char *queuename = NULL, *interface;
08294    int penalty = 0;
08295 
08296    switch (cmd) {
08297    case CLI_INIT:
08298       e->command = "queue set penalty";
08299       e->usage = 
08300       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
08301       "  Set a member's penalty in the queue specified. If no queue is specified\n"
08302       "  then that interface's penalty is set in all queues to which that interface is a member\n";
08303       return NULL;
08304    case CLI_GENERATE:
08305       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
08306    }
08307 
08308    if (a->argc != 6 && a->argc != 8) {
08309       return CLI_SHOWUSAGE;
08310    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
08311       return CLI_SHOWUSAGE;
08312    }
08313 
08314    if (a->argc == 8)
08315       queuename = a->argv[7];
08316    interface = a->argv[5];
08317    penalty = atoi(a->argv[3]);
08318 
08319    switch (set_member_penalty(queuename, interface, penalty)) {
08320    case RESULT_SUCCESS:
08321       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08322       return CLI_SUCCESS;
08323    case RESULT_FAILURE:
08324       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08325       return CLI_FAILURE;
08326    default:
08327       return CLI_FAILURE;
08328    }
08329 }

static int handle_statechange ( void *  datap  )  [static]

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

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

01562 {
01563    struct statechange *sc = datap;
01564    struct ao2_iterator miter, qiter;
01565    struct member *m;
01566    struct call_queue *q;
01567    char interface[80], *slash_pos;
01568    int found = 0;
01569 
01570    qiter = ao2_iterator_init(queues, 0);
01571    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01572       ao2_lock(q);
01573 
01574       miter = ao2_iterator_init(q->members, 0);
01575       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01576          ast_copy_string(interface, m->state_interface, sizeof(interface));
01577 
01578          if ((slash_pos = strchr(interface, '/')))
01579             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01580                *slash_pos = '\0';
01581 
01582          if (!strcasecmp(interface, sc->dev)) {
01583             found = 1;
01584             update_status(q, m, sc->state);
01585             ao2_ref(m, -1);
01586             break;
01587          }
01588       }
01589       ao2_iterator_destroy(&miter);
01590 
01591       ao2_unlock(q);
01592       queue_t_unref(q, "Done with iterator");
01593    }
01594    ao2_iterator_destroy(&qiter);
01595 
01596    if (found)
01597       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01598    else
01599       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));
01600 
01601    ast_free(sc);
01602    return 0;
01603 }

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

02992 {
02993    struct callattempt *oo;
02994 
02995    while (outgoing) {
02996       /* If someone else answered the call we should indicate this in the CANCEL */
02997       /* Hangup any existing lines we have open */
02998       if (outgoing->chan && (outgoing->chan != exception)) {
02999          if (exception || cancel_answered_elsewhere)
03000             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03001          ast_hangup(outgoing->chan);
03002       }
03003       oo = outgoing;
03004       outgoing = outgoing->q_next;
03005       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
03006       callattempt_free(oo);
03007    }
03008 }

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

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

Referenced by find_queue_by_name_rt(), and reload_single_queue().

01772 {
01773    int i;
01774    struct penalty_rule *pr_iter;
01775 
01776    q->dead = 0;
01777    q->retry = DEFAULT_RETRY;
01778    q->timeout = DEFAULT_TIMEOUT;
01779    q->maxlen = 0;
01780    q->announcefrequency = 0;
01781    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01782    q->announceholdtime = 1;
01783    q->announcepositionlimit = 10; /* Default 10 positions */
01784    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01785    q->roundingseconds = 0; /* Default - don't announce seconds */
01786    q->servicelevel = 0;
01787    q->ringinuse = 1;
01788    q->announce_to_first_user = 0;
01789    q->setinterfacevar = 0;
01790    q->setqueuevar = 0;
01791    q->setqueueentryvar = 0;
01792    q->autofill = autofill_default;
01793    q->montype = montype_default;
01794    q->monfmt[0] = '\0';
01795    q->reportholdtime = 0;
01796    q->wrapuptime = 0;
01797    q->penaltymemberslimit = 0;
01798    q->joinempty = 0;
01799    q->leavewhenempty = 0;
01800    q->memberdelay = 0;
01801    q->maskmemberstatus = 0;
01802    q->eventwhencalled = 0;
01803    q->weight = 0;
01804    q->timeoutrestart = 0;
01805    q->periodicannouncefrequency = 0;
01806    q->randomperiodicannounce = 0;
01807    q->numperiodicannounce = 0;
01808    q->autopause = QUEUE_AUTOPAUSE_OFF;
01809    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01810    if (!q->members) {
01811       if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
01812          /* linear strategy depends on order, so we have to place all members in a single bucket */
01813          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01814       else
01815          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01816    }
01817    q->found = 1;
01818 
01819    ast_string_field_set(q, sound_next, "queue-youarenext");
01820    ast_string_field_set(q, sound_thereare, "queue-thereare");
01821    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01822    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01823    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01824    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01825    ast_string_field_set(q, sound_minutes, "queue-minutes");
01826    ast_string_field_set(q, sound_minute, "queue-minute");
01827    ast_string_field_set(q, sound_seconds, "queue-seconds");
01828    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01829    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01830 
01831    if (!q->sound_periodicannounce[0]) {
01832       q->sound_periodicannounce[0] = ast_str_create(32);
01833    }
01834 
01835    if (q->sound_periodicannounce[0]) {
01836       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01837    }
01838 
01839    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01840       if (q->sound_periodicannounce[i])
01841          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01842    }
01843 
01844    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01845       ast_free(pr_iter);
01846 }

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

References call_queue::head, and queue_ref().

Referenced by join_queue().

01422 {
01423    struct queue_ent *cur;
01424 
01425    if (!q || !new)
01426       return;
01427    if (prev) {
01428       cur = prev->next;
01429       prev->next = new;
01430    } else {
01431       cur = q->head;
01432       q->head = new;
01433    }
01434    new->next = cur;
01435 
01436    /* every queue_ent must have a reference to it's parent call_queue, this
01437     * reference does not go away until the end of the queue_ent's life, meaning
01438     * that even when the queue_ent leaves the call_queue this ref must remain. */
01439    queue_ref(q);
01440    new->parent = q;
01441    new->pos = ++(*pos);
01442    new->opos = *pos;
01443 }

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

01878 {
01879    char *timestr, *maxstr, *minstr, *contentdup;
01880    struct penalty_rule *rule = NULL, *rule_iter;
01881    struct rule_list *rl_iter;
01882    int penaltychangetime, inserted = 0;
01883 
01884    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01885       return -1;
01886    }
01887 
01888    contentdup = ast_strdupa(content);
01889    
01890    if (!(maxstr = strchr(contentdup, ','))) {
01891       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01892       ast_free(rule);
01893       return -1;
01894    }
01895 
01896    *maxstr++ = '\0';
01897    timestr = contentdup;
01898 
01899    if ((penaltychangetime = atoi(timestr)) < 0) {
01900       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01901       ast_free(rule);
01902       return -1;
01903    }
01904 
01905    rule->time = penaltychangetime;
01906 
01907    if ((minstr = strchr(maxstr,',')))
01908       *minstr++ = '\0';
01909    
01910    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01911     * OR if a min penalty change is indicated but no max penalty change is */
01912    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01913       rule->max_relative = 1;
01914    }
01915 
01916    rule->max_value = atoi(maxstr);
01917 
01918    if (!ast_strlen_zero(minstr)) {
01919       if (*minstr == '+' || *minstr == '-')
01920          rule->min_relative = 1;
01921       rule->min_value = atoi(minstr);
01922    } else /*there was no minimum specified, so assume this means no change*/
01923       rule->min_relative = 1;
01924 
01925    /*We have the rule made, now we need to insert it where it belongs*/
01926    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01927       if (strcasecmp(rl_iter->name, list_name))
01928          continue;
01929 
01930       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01931          if (rule->time < rule_iter->time) {
01932             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01933             inserted = 1;
01934             break;
01935          }
01936       }
01937       AST_LIST_TRAVERSE_SAFE_END;
01938    
01939       if (!inserted) {
01940          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01941          inserted = 1;
01942       }
01943 
01944       break;
01945    }
01946 
01947    if (!inserted) {
01948       ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name);
01949       ast_free(rule);
01950       return -1;
01951    }
01952    return 0;
01953 }

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

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

01260 {
01261    int x;
01262 
01263    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01264       if (strategy == strategies[x].strategy)
01265          return strategies[x].name;
01266    }
01267 
01268    return "<unknown>";
01269 }

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

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

05486 {
05487    struct member *mem;
05488    struct ao2_iterator mem_iter;
05489 
05490    if (!q)
05491       return NULL;
05492 
05493    mem_iter = ao2_iterator_init(q->members, 0);
05494    while ((mem = ao2_iterator_next(&mem_iter))) {
05495       if (!strcasecmp(interface, mem->interface)) {
05496          ao2_iterator_destroy(&mem_iter);
05497          return mem;
05498       }
05499       ao2_ref(mem, -1);
05500    }
05501    ao2_iterator_destroy(&mem_iter);
05502 
05503    return NULL;
05504 }

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

04186 {
04187    struct queue_ent *ch;
04188    int res;
04189    int avl;
04190    int idx = 0;
04191    /* This needs a lock. How many members are available to be served? */
04192    ao2_lock(qe->parent);
04193 
04194    avl = num_available_members(qe->parent);
04195 
04196    ch = qe->parent->head;
04197 
04198    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
04199 
04200    while ((idx < avl) && (ch) && (ch != qe)) {
04201       if (!ch->pending)
04202          idx++;
04203       ch = ch->next;       
04204    }
04205 
04206    ao2_unlock(qe->parent);
04207    /* If the queue entry is within avl [the number of available members] calls from the top ... 
04208     * Autofill and position check added to support autofill=no (as only calls
04209     * from the front of the queue are valid when autofill is disabled)
04210     */
04211    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
04212       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
04213       res = 1;
04214    } else {
04215       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
04216       res = 0;
04217    }
04218 
04219    return res;
04220 }

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

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

02602 {
02603    struct call_queue *q;
02604    struct queue_ent *cur, *prev = NULL;
02605    int res = -1;
02606    int pos = 0;
02607    int inserted = 0;
02608 
02609    if (!(q = load_realtime_queue(queuename)))
02610       return res;
02611 
02612    ao2_lock(q);
02613 
02614    /* This is our one */
02615    if (q->joinempty) {
02616       int status = 0;
02617       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty, 0))) {
02618          *reason = QUEUE_JOINEMPTY;
02619          ao2_unlock(q);
02620          queue_t_unref(q, "Done with realtime queue");
02621          return res;
02622       }
02623    }
02624    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02625       *reason = QUEUE_FULL;
02626    else if (*reason == QUEUE_UNKNOWN) {
02627       /* There's space for us, put us at the right position inside
02628        * the queue.
02629        * Take into account the priority of the calling user */
02630       inserted = 0;
02631       prev = NULL;
02632       cur = q->head;
02633       while (cur) {
02634          /* We have higher priority than the current user, enter
02635           * before him, after all the other users with priority
02636           * higher or equal to our priority. */
02637          if ((!inserted) && (qe->prio > cur->prio)) {
02638             insert_entry(q, prev, qe, &pos);
02639             inserted = 1;
02640          }
02641          /* <= is necessary for the position comparison because it may not be possible to enter
02642           * at our desired position since higher-priority callers may have taken the position we want
02643           */
02644          if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02645             insert_entry(q, prev, qe, &pos);
02646             inserted = 1;
02647             /*pos is incremented inside insert_entry, so don't need to add 1 here*/
02648             if (position < pos) {
02649                ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02650             }
02651          }
02652          cur->pos = ++pos;
02653          prev = cur;
02654          cur = cur->next;
02655       }
02656       /* No luck, join at the end of the queue */
02657       if (!inserted)
02658          insert_entry(q, prev, qe, &pos);
02659       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02660       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02661       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02662       q->count++;
02663       res = 0;
02664       ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02665          "Channel: %s\r\n"
02666          "CallerIDNum: %s\r\n"
02667          "CallerIDName: %s\r\n"
02668          "ConnectedLineNum: %s\r\n"
02669          "ConnectedLineName: %s\r\n"
02670          "Queue: %s\r\n"
02671          "Position: %d\r\n"
02672          "Count: %d\r\n"
02673          "Uniqueid: %s\r\n",
02674          qe->chan->name,
02675          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02676          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02677          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02678          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
02679          q->name, qe->pos, q->count, qe->chan->uniqueid );
02680       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02681    }
02682    ao2_unlock(q);
02683    queue_t_unref(q, "Done with realtime queue");
02684 
02685    return res;
02686 }

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

Definition at line 7017 of file app_queue.c.

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

Referenced by reload_single_queue().

07018 {
07019    struct member *member = obj;
07020 
07021    if (!member->delme) {
07022       member->status = get_queue_member_status(member);
07023       return 0;
07024    } else {
07025       return CMP_MATCH;
07026    }
07027 }

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

Definition at line 7161 of file app_queue.c.

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

Referenced by reload_queues().

07162 {
07163    struct call_queue *q = obj;
07164    char *queuename = arg;
07165    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
07166       return CMP_MATCH;
07167    } else {
07168       return 0;
07169    }
07170 }

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

02914 {
02915    struct call_queue *q;
02916    struct queue_ent *current, *prev = NULL;
02917    struct penalty_rule *pr_iter;
02918    int pos = 0;
02919 
02920    if (!(q = qe->parent))
02921       return;
02922    queue_t_ref(q, "Copy queue pointer from queue entry");
02923    ao2_lock(q);
02924 
02925    prev = NULL;
02926    for (current = q->head; current; current = current->next) {
02927       if (current == qe) {
02928          char posstr[20];
02929          q->count--;
02930 
02931          /* Take us out of the queue */
02932          ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
02933             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
02934             qe->chan->name, q->name,  q->count, qe->pos, qe->chan->uniqueid);
02935          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02936          /* Take us out of the queue */
02937          if (prev)
02938             prev->next = current->next;
02939          else
02940             q->head = current->next;
02941          /* Free penalty rules */
02942          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02943             ast_free(pr_iter);
02944          snprintf(posstr, sizeof(posstr), "%d", qe->pos);
02945          pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
02946       } else {
02947          /* Renumber the people after us in the queue based on a new count */
02948          current->pos = ++pos;
02949          prev = current;
02950       }
02951    }
02952    ao2_unlock(q);
02953 
02954    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02955    if (q->realtime) {
02956       struct ast_variable *var;
02957       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02958          q->dead = 1;
02959       } else {
02960          ast_variables_destroy(var);
02961       }
02962    }
02963 
02964    if (q->dead) { 
02965       /* It's dead and nobody is in it, so kill it */
02966       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02967    }
02968    /* unref the explicit ref earlier in the function */
02969    queue_t_unref(q, "Expire copied reference");
02970 }

static int load_module ( void   )  [static]

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

08839 {
08840    int res;
08841    struct ast_context *con;
08842    struct ast_flags mask = {AST_FLAGS_ALL, };
08843 
08844    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08845 
08846    use_weight = 0;
08847 
08848    if (reload_handler(0, &mask, NULL))
08849       return AST_MODULE_LOAD_DECLINE;
08850 
08851    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08852    if (!con)
08853       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08854    else
08855       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08856 
08857    if (queue_persistent_members)
08858       reload_queue_members();
08859 
08860    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08861 
08862    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08863    res = ast_register_application_xml(app, queue_exec);
08864    res |= ast_register_application_xml(app_aqm, aqm_exec);
08865    res |= ast_register_application_xml(app_rqm, rqm_exec);
08866    res |= ast_register_application_xml(app_pqm, pqm_exec);
08867    res |= ast_register_application_xml(app_upqm, upqm_exec);
08868    res |= ast_register_application_xml(app_ql, ql_exec);
08869    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08870    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08871    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08872    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08873    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08874    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08875    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08876    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08877    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08878    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08879    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08880    res |= ast_custom_function_register(&queuevar_function);
08881    res |= ast_custom_function_register(&queueexists_function);
08882    res |= ast_custom_function_register(&queuemembercount_function);
08883    res |= ast_custom_function_register(&queuemembercount_dep);
08884    res |= ast_custom_function_register(&queuememberlist_function);
08885    res |= ast_custom_function_register(&queuewaitingcount_function);
08886    res |= ast_custom_function_register(&queuememberpenalty_function);
08887 
08888    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08889       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08890    }
08891 
08892    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08893    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08894       res = -1;
08895    }
08896 
08897    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08898 
08899    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08900 
08901    return res ? AST_MODULE_LOAD_DECLINE : 0;
08902 }

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

02475 {
02476    struct ast_variable *queue_vars;
02477    struct ast_config *member_config = NULL;
02478    struct call_queue *q = NULL, tmpq = {
02479       .name = queuename,   
02480    };
02481    int prev_weight = 0;
02482 
02483    /* Find the queue in the in-core list first. */
02484    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02485 
02486    if (!q || q->realtime) {
02487       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
02488          queue operations while waiting for the DB.
02489 
02490          This will be two separate database transactions, so we might
02491          see queue parameters as they were before another process
02492          changed the queue and member list as it was after the change.
02493          Thus we might see an empty member list when a queue is
02494          deleted. In practise, this is unlikely to cause a problem. */
02495 
02496       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02497       if (queue_vars) {
02498          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02499          if (!member_config) {
02500             ast_debug(1, "No queue_members defined in config extconfig.conf\n");
02501             member_config = ast_config_new();
02502          }
02503       }
02504       if (q) {
02505          prev_weight = q->weight ? 1 : 0;
02506          queue_t_unref(q, "Need to find realtime queue");
02507       }
02508 
02509       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02510       ast_config_destroy(member_config);
02511       ast_variables_destroy(queue_vars);
02512 
02513       /* update the use_weight value if the queue's has gained or lost a weight */
02514       if (q) {
02515          if (!q->weight && prev_weight) {
02516             ast_atomic_fetchadd_int(&use_weight, -1);
02517          }
02518          if (q->weight && !prev_weight) {
02519             ast_atomic_fetchadd_int(&use_weight, +1);
02520          }
02521       }
02522       /* Other cases will end up with the proper value for use_weight */
02523    } else {
02524       update_realtime_members(q);
02525    }
02526    return q;
02527 }

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

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

07800 {
07801    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07802    int paused, penalty = 0;
07803 
07804    queuename = astman_get_header(m, "Queue");
07805    interface = astman_get_header(m, "Interface");
07806    penalty_s = astman_get_header(m, "Penalty");
07807    paused_s = astman_get_header(m, "Paused");
07808    membername = astman_get_header(m, "MemberName");
07809    state_interface = astman_get_header(m, "StateInterface");
07810 
07811    if (ast_strlen_zero(queuename)) {
07812       astman_send_error(s, m, "'Queue' not specified.");
07813       return 0;
07814    }
07815 
07816    if (ast_strlen_zero(interface)) {
07817       astman_send_error(s, m, "'Interface' not specified.");
07818       return 0;
07819    }
07820 
07821    if (ast_strlen_zero(penalty_s))
07822       penalty = 0;
07823    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07824       penalty = 0;
07825 
07826    if (ast_strlen_zero(paused_s))
07827       paused = 0;
07828    else
07829       paused = abs(ast_true(paused_s));
07830 
07831    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07832    case RES_OKAY:
07833       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07834       astman_send_ack(s, m, "Added interface to queue");
07835       break;
07836    case RES_EXISTS:
07837       astman_send_error(s, m, "Unable to add interface: Already there");
07838       break;
07839    case RES_NOSUCHQUEUE:
07840       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07841       break;
07842    case RES_OUTOFMEMORY:
07843       astman_send_error(s, m, "Out of memory");
07844       break;
07845    }
07846 
07847    return 0;
07848 }

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

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

07885 {
07886    const char *queuename, *interface, *paused_s, *reason;
07887    int paused;
07888 
07889    interface = astman_get_header(m, "Interface");
07890    paused_s = astman_get_header(m, "Paused");
07891    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07892    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07893 
07894    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07895       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07896       return 0;
07897    }
07898 
07899    paused = abs(ast_true(paused_s));
07900 
07901    if (set_member_paused(queuename, interface, reason, paused))
07902       astman_send_error(s, m, "Interface not found");
07903    else
07904       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07905    return 0;
07906 }

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

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

07909 {
07910    const char *queuename, *event, *message, *interface, *uniqueid;
07911 
07912    queuename = astman_get_header(m, "Queue");
07913    uniqueid = astman_get_header(m, "UniqueId");
07914    interface = astman_get_header(m, "Interface");
07915    event = astman_get_header(m, "Event");
07916    message = astman_get_header(m, "Message");
07917 
07918    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07919       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07920       return 0;
07921    }
07922 
07923    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07924    astman_send_ack(s, m, "Event added successfully");
07925 
07926    return 0;
07927 }

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

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

08008 {
08009    const char *queuename, *interface, *penalty_s;
08010    int penalty;
08011 
08012    interface = astman_get_header(m, "Interface");
08013    penalty_s = astman_get_header(m, "Penalty");
08014    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
08015    queuename = astman_get_header(m, "Queue");
08016 
08017    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
08018       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
08019       return 0;
08020    }
08021  
08022    penalty = atoi(penalty_s);
08023 
08024    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
08025       astman_send_error(s, m, "Invalid interface, queuename or penalty");
08026    else
08027       astman_send_ack(s, m, "Interface penalty set successfully");
08028 
08029    return 0;
08030 }

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

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

07930 {
07931    struct ast_flags mask = {0,};
07932    const char *queuename = NULL;
07933    int header_found = 0;
07934 
07935    queuename = astman_get_header(m, "Queue");
07936    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07937       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07938       header_found = 1;
07939    }
07940    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07941       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07942       header_found = 1;
07943    }
07944    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07945       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07946       header_found = 1;
07947    }
07948 
07949    if (!header_found) {
07950       ast_set_flag(&mask, AST_FLAGS_ALL);
07951    }
07952 
07953    if (!reload_handler(1, &mask, queuename)) {
07954       astman_send_ack(s, m, "Queue reloaded successfully");
07955    } else {
07956       astman_send_error(s, m, "Error encountered while reloading queue");
07957    }
07958    return 0;
07959 }

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

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

07962 {
07963    const char *queuename = NULL;
07964    struct ast_flags mask = {QUEUE_RESET_STATS,};
07965    
07966    queuename = astman_get_header(m, "Queue");
07967 
07968    if (!reload_handler(1, &mask, queuename)) {
07969       astman_send_ack(s, m, "Queue stats reset successfully");
07970    } else {
07971       astman_send_error(s, m, "Error encountered while resetting queue stats");
07972    }
07973    return 0;
07974 }

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

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

07589 {
07590    const char *rule = astman_get_header(m, "Rule");
07591    const char *id = astman_get_header(m, "ActionID");
07592    struct rule_list *rl_iter;
07593    struct penalty_rule *pr_iter;
07594 
07595    astman_append(s, "Response: Success\r\n");
07596    if (!ast_strlen_zero(id)) {
07597       astman_append(s, "ActionID: %s\r\n", id);
07598    }
07599 
07600    AST_LIST_LOCK(&rule_lists);
07601    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07602       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07603          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07604          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07605             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 );
07606          }
07607          if (!ast_strlen_zero(rule))
07608             break;
07609       }
07610    }
07611    AST_LIST_UNLOCK(&rule_lists);
07612 
07613    /*
07614     * Two blank lines instead of one because the Response and
07615     * ActionID headers used to not be present.
07616     */
07617    astman_append(s, "\r\n\r\n");
07618 
07619    return RESULT_SUCCESS;
07620 }

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

Definition at line 7578 of file app_queue.c.

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

Referenced by load_module().

07579 {
07580    static const char * const a[] = { "queue", "show" };
07581 
07582    __queues_show(s, -1, 2, a);
07583    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07584 
07585    return RESULT_SUCCESS;
07586 }

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

Queue status info via AMI.

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

07699 {
07700    time_t now;
07701    int pos;
07702    const char *id = astman_get_header(m,"ActionID");
07703    const char *queuefilter = astman_get_header(m,"Queue");
07704    const char *memberfilter = astman_get_header(m,"Member");
07705    char idText[256] = "";
07706    struct call_queue *q;
07707    struct queue_ent *qe;
07708    float sl = 0;
07709    struct member *mem;
07710    struct ao2_iterator queue_iter;
07711    struct ao2_iterator mem_iter;
07712 
07713    astman_send_ack(s, m, "Queue status will follow");
07714    time(&now);
07715    if (!ast_strlen_zero(id))
07716       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07717 
07718    queue_iter = ao2_iterator_init(queues, 0);
07719    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07720       ao2_lock(q);
07721 
07722       /* List queue properties */
07723       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07724          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07725          astman_append(s, "Event: QueueParams\r\n"
07726             "Queue: %s\r\n"
07727             "Max: %d\r\n"
07728             "Strategy: %s\r\n"
07729             "Calls: %d\r\n"
07730             "Holdtime: %d\r\n"
07731             "TalkTime: %d\r\n"
07732             "Completed: %d\r\n"
07733             "Abandoned: %d\r\n"
07734             "ServiceLevel: %d\r\n"
07735             "ServicelevelPerf: %2.1f\r\n"
07736             "Weight: %d\r\n"
07737             "%s"
07738             "\r\n",
07739             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07740             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07741          /* List Queue Members */
07742          mem_iter = ao2_iterator_init(q->members, 0);
07743          while ((mem = ao2_iterator_next(&mem_iter))) {
07744             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07745                astman_append(s, "Event: QueueMember\r\n"
07746                   "Queue: %s\r\n"
07747                   "Name: %s\r\n"
07748                   "Location: %s\r\n"
07749                   "Membership: %s\r\n"
07750                   "Penalty: %d\r\n"
07751                   "CallsTaken: %d\r\n"
07752                   "LastCall: %d\r\n"
07753                   "Status: %d\r\n"
07754                   "Paused: %d\r\n"
07755                   "%s"
07756                   "\r\n",
07757                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07758                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07759             }
07760             ao2_ref(mem, -1);
07761          }
07762          ao2_iterator_destroy(&mem_iter);
07763          /* List Queue Entries */
07764          pos = 1;
07765          for (qe = q->head; qe; qe = qe->next) {
07766             astman_append(s, "Event: QueueEntry\r\n"
07767                "Queue: %s\r\n"
07768                "Position: %d\r\n"
07769                "Channel: %s\r\n"
07770                "Uniqueid: %s\r\n"
07771                "CallerIDNum: %s\r\n"
07772                "CallerIDName: %s\r\n"
07773                "ConnectedLineNum: %s\r\n"
07774                "ConnectedLineName: %s\r\n"
07775                "Wait: %ld\r\n"
07776                "%s"
07777                "\r\n",
07778                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07779                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07780                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07781                S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07782                S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07783                (long) (now - qe->start), idText);
07784          }
07785       }
07786       ao2_unlock(q);
07787       queue_t_unref(q, "Done with iterator");
07788    }
07789    ao2_iterator_destroy(&queue_iter);
07790 
07791    astman_append(s,
07792       "Event: QueueStatusComplete\r\n"
07793       "%s"
07794       "\r\n",idText);
07795 
07796    return RESULT_SUCCESS;
07797 }

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

Summary of queue info via the AMI.

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

07624 {
07625    time_t now;
07626    int qmemcount = 0;
07627    int qmemavail = 0;
07628    int qchancount = 0;
07629    int qlongestholdtime = 0;
07630    const char *id = astman_get_header(m, "ActionID");
07631    const char *queuefilter = astman_get_header(m, "Queue");
07632    char idText[256] = "";
07633    struct call_queue *q;
07634    struct queue_ent *qe;
07635    struct member *mem;
07636    struct ao2_iterator queue_iter;
07637    struct ao2_iterator mem_iter;
07638 
07639    astman_send_ack(s, m, "Queue summary will follow");
07640    time(&now);
07641    if (!ast_strlen_zero(id))
07642       snprintf(idText, 256, "ActionID: %s\r\n", id);
07643    queue_iter = ao2_iterator_init(queues, 0);
07644    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07645       ao2_lock(q);
07646 
07647       /* List queue properties */
07648       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07649          /* Reset the necessary local variables if no queuefilter is set*/
07650          qmemcount = 0;
07651          qmemavail = 0;
07652          qchancount = 0;
07653          qlongestholdtime = 0;
07654 
07655          /* List Queue Members */
07656          mem_iter = ao2_iterator_init(q->members, 0);
07657          while ((mem = ao2_iterator_next(&mem_iter))) {
07658             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07659                ++qmemcount;
07660                if (member_status_available(mem->status) && !mem->paused) {
07661                   ++qmemavail;
07662                }
07663             }
07664             ao2_ref(mem, -1);
07665          }
07666          ao2_iterator_destroy(&mem_iter);
07667          for (qe = q->head; qe; qe = qe->next) {
07668             if ((now - qe->start) > qlongestholdtime) {
07669                qlongestholdtime = now - qe->start;
07670             }
07671             ++qchancount;
07672          }
07673          astman_append(s, "Event: QueueSummary\r\n"
07674             "Queue: %s\r\n"
07675             "LoggedIn: %d\r\n"
07676             "Available: %d\r\n"
07677             "Callers: %d\r\n" 
07678             "HoldTime: %d\r\n"
07679             "TalkTime: %d\r\n"
07680             "LongestHoldTime: %d\r\n"
07681             "%s"
07682             "\r\n",
07683             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07684       }
07685       ao2_unlock(q);
07686       queue_t_unref(q, "Done with iterator");
07687    }
07688    ao2_iterator_destroy(&queue_iter);
07689    astman_append(s,
07690       "Event: QueueSummaryComplete\r\n"
07691       "%s"
07692       "\r\n", idText);
07693 
07694    return RESULT_SUCCESS;
07695 }

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

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

07851 {
07852    const char *queuename, *interface;
07853 
07854    queuename = astman_get_header(m, "Queue");
07855    interface = astman_get_header(m, "Interface");
07856 
07857    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07858       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07859       return 0;
07860    }
07861 
07862    switch (remove_from_queue(queuename, interface)) {
07863    case RES_OKAY:
07864       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07865       astman_send_ack(s, m, "Removed interface from queue");
07866       break;
07867    case RES_EXISTS:
07868       astman_send_error(s, m, "Unable to remove interface: Not there");
07869       break;
07870    case RES_NOSUCHQUEUE:
07871       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07872       break;
07873    case RES_OUTOFMEMORY:
07874       astman_send_error(s, m, "Out of memory");
07875       break;
07876    case RES_NOT_DYNAMIC:
07877       astman_send_error(s, m, "Member not dynamic");
07878       break;
07879    }
07880 
07881    return 0;
07882 }

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

Definition at line 7150 of file app_queue.c.

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

Referenced by reload_queues().

07151 {
07152    struct call_queue *q = obj;
07153    char *queuename = arg;
07154    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07155       q->dead = 1;
07156       q->found = 0;
07157    }
07158    return 0;
07159 }

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

Definition at line 7008 of file app_queue.c.

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

Referenced by reload_single_queue().

07009 {
07010    struct member *member = obj;
07011    if (!member->dynamic && !member->realtime) {
07012       member->delme = 1;
07013    }
07014    return 0;
07015 }

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

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

02202 {
02203    ao2_lock(queue->members);
02204    mem->queuepos = ao2_container_count(queue->members);
02205    ao2_link(queue->members, mem);
02206    ao2_unlock(queue->members);
02207 }

static void member_call_pending_clear ( struct member mem  )  [static]

Definition at line 3162 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry(), and ring_entry().

03163 {
03164    ao2_lock(mem);
03165    mem->call_pending = 0;
03166    ao2_unlock(mem);
03167 }

static int member_call_pending_set ( struct member mem  )  [static]

Definition at line 3177 of file app_queue.c.

References ao2_lock, ao2_unlock, and member::call_pending.

Referenced by can_ring_entry().

03178 {
03179    int old_pending;
03180 
03181    ao2_lock(mem);
03182    old_pending = mem->call_pending;
03183    mem->call_pending = 1;
03184    ao2_unlock(mem);
03185 
03186    return old_pending;
03187 }

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

Definition at line 1761 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01762 {
01763    struct member *mem1 = obj1, *mem2 = obj2;
01764    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01765 }

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

Definition at line 1749 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01750 {
01751    const struct member *mem = obj;
01752    const char *chname = strchr(mem->interface, '/');
01753    int ret = 0, i;
01754    if (!chname)
01755       chname = mem->interface;
01756    for (i = 0; i < 5 && chname[i]; i++)
01757       ret += compress_char(chname[i]) << (i * 6);
01758    return ret;
01759 }

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

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

02216 {
02217    ao2_lock(queue->members);
02218    queue_member_follower_removal(queue, mem);
02219    ao2_unlink(queue->members, mem);
02220    ao2_unlock(queue->members);
02221 }

static int member_status_available ( int  status  )  [static]

Definition at line 3149 of file app_queue.c.

References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.

Referenced by can_ring_entry(), and manager_queues_summary().

03150 {
03151    return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN;
03152 }

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

03019 {
03020    struct member *mem;
03021    int avl = 0;
03022    struct ao2_iterator mem_iter;
03023 
03024    mem_iter = ao2_iterator_init(q->members, 0);
03025    while ((mem = ao2_iterator_next(&mem_iter))) {
03026       switch (mem->status) {
03027       case AST_DEVICE_INUSE:
03028          if (!q->ringinuse)
03029             break;
03030          /* else fall through */
03031       case AST_DEVICE_NOT_INUSE:
03032       case AST_DEVICE_UNKNOWN:
03033          if (!mem->paused) {
03034             avl++;
03035          }
03036          break;
03037       }
03038       ao2_ref(mem, -1);
03039 
03040       /* If autofill is not enabled or if the queue's strategy is ringall, then
03041        * we really don't care about the number of available members so much as we
03042        * do that there is at least one available.
03043        *
03044        * In fact, we purposely will return from this function stating that only
03045        * one member is available if either of those conditions hold. That way,
03046        * functions which determine what action to take based on the number of available
03047        * members will operate properly. The reasoning is that even if multiple
03048        * members are available, only the head caller can actually be serviced.
03049        */
03050       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
03051          break;
03052       }
03053    }
03054    ao2_iterator_destroy(&mem_iter);
03055 
03056    return avl;
03057 }

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

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

01956 {
01957    char *value_copy = ast_strdupa(value);
01958    char *option = NULL;
01959    while ((option = strsep(&value_copy, ","))) {
01960       if (!strcasecmp(option, "paused")) {
01961          *empty |= QUEUE_EMPTY_PAUSED;
01962       } else if (!strcasecmp(option, "penalty")) {
01963          *empty |= QUEUE_EMPTY_PENALTY;
01964       } else if (!strcasecmp(option, "inuse")) {
01965          *empty |= QUEUE_EMPTY_INUSE;
01966       } else if (!strcasecmp(option, "ringing")) {
01967          *empty |= QUEUE_EMPTY_RINGING;
01968       } else if (!strcasecmp(option, "invalid")) {
01969          *empty |= QUEUE_EMPTY_INVALID;
01970       } else if (!strcasecmp(option, "wrapup")) {
01971          *empty |= QUEUE_EMPTY_WRAPUP;
01972       } else if (!strcasecmp(option, "unavailable")) {
01973          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01974       } else if (!strcasecmp(option, "unknown")) {
01975          *empty |= QUEUE_EMPTY_UNKNOWN;
01976       } else if (!strcasecmp(option, "loose")) {
01977          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01978       } else if (!strcasecmp(option, "strict")) {
01979          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01980       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01981          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01982       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01983          *empty = 0;
01984       } else {
01985          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01986       }
01987    }
01988 }

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

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

02689 {
02690    int res;
02691 
02692    if (ast_strlen_zero(filename)) {
02693       return 0;
02694    }
02695 
02696    if (!ast_fileexists(filename, NULL, chan->language)) {
02697       return 0;
02698    }
02699 
02700    ast_stopstream(chan);
02701 
02702    res = ast_streamfile(chan, filename, chan->language);
02703    if (!res)
02704       res = ast_waitstream(chan, AST_DIGIT_ANY);
02705 
02706    ast_stopstream(chan);
02707 
02708    return res;
02709 }

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

PauseQueueMember application.

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

05916 {
05917    char *parse;
05918    AST_DECLARE_APP_ARGS(args,
05919       AST_APP_ARG(queuename);
05920       AST_APP_ARG(interface);
05921       AST_APP_ARG(options);
05922       AST_APP_ARG(reason);
05923    );
05924 
05925    if (ast_strlen_zero(data)) {
05926       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05927       return -1;
05928    }
05929 
05930    parse = ast_strdupa(data);
05931 
05932    AST_STANDARD_APP_ARGS(args, parse);
05933 
05934    if (ast_strlen_zero(args.interface)) {
05935       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05936       return -1;
05937    }
05938 
05939    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05940       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05941       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05942       return 0;
05943    }
05944 
05945    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05946 
05947    return 0;
05948 }

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

QueueLog application.

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

06107 {
06108    char *parse;
06109 
06110    AST_DECLARE_APP_ARGS(args,
06111       AST_APP_ARG(queuename);
06112       AST_APP_ARG(uniqueid);
06113       AST_APP_ARG(membername);
06114       AST_APP_ARG(event);
06115       AST_APP_ARG(params);
06116    );
06117 
06118    if (ast_strlen_zero(data)) {
06119       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
06120       return -1;
06121    }
06122 
06123    parse = ast_strdupa(data);
06124 
06125    AST_STANDARD_APP_ARGS(args, parse);
06126 
06127    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
06128        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
06129       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
06130       return -1;
06131    }
06132 
06133    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
06134       "%s", args.params ? args.params : "");
06135 
06136    return 0;
06137 }

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

Definition at line 1310 of file app_queue.c.

References CMP_MATCH, and CMP_STOP.

Referenced by load_module().

01311 {
01312    struct call_queue *q = obj, *q2 = arg;
01313    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
01314 }

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

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

01340 {
01341    struct member *mem = obj;
01342    struct call_queue *queue = arg;
01343    int rrpos = mem->queuepos;
01344 
01345    if (mem->delme) {
01346       ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &rrpos);
01347    }
01348 
01349    return 0;
01350 }

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

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

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

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

06527 {
06528    struct call_queue *q;
06529 
06530    buf[0] = '\0';
06531 
06532    if (ast_strlen_zero(data)) {
06533       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06534       return -1;
06535    }
06536    q = load_realtime_queue(data);
06537    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06538    if (q) {
06539       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06540    }
06541 
06542    return 0;
06543 }

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

06746 {
06747    int penalty;
06748    AST_DECLARE_APP_ARGS(args,
06749       AST_APP_ARG(queuename);
06750       AST_APP_ARG(interface);
06751    );
06752    /* Make sure the returned value on error is NULL. */
06753    buf[0] = '\0';
06754 
06755    if (ast_strlen_zero(data)) {
06756       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06757       return -1;
06758    }
06759 
06760    AST_STANDARD_APP_ARGS(args, data);
06761 
06762    if (args.argc < 2) {
06763       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06764       return -1;
06765    }
06766 
06767    penalty = get_member_penalty (args.queuename, args.interface);
06768    
06769    if (penalty >= 0) /* remember that buf is already '\0' */
06770       snprintf (buf, len, "%d", penalty);
06771 
06772    return 0;
06773 }

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

06777 {
06778    int penalty;
06779    AST_DECLARE_APP_ARGS(args,
06780       AST_APP_ARG(queuename);
06781       AST_APP_ARG(interface);
06782    );
06783 
06784    if (ast_strlen_zero(data)) {
06785       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06786       return -1;
06787    }
06788 
06789    AST_STANDARD_APP_ARGS(args, data);
06790 
06791    if (args.argc < 2) {
06792       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06793       return -1;
06794    }
06795 
06796    penalty = atoi(value);
06797 
06798    if (ast_strlen_zero(args.interface)) {
06799       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06800       return -1;
06801    }
06802 
06803    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06804    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06805       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06806       return -1;
06807    }
06808 
06809    return 0;
06810 }

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

06551 {
06552    int count = 0;
06553    struct member *m;
06554    struct ao2_iterator mem_iter;
06555    struct call_queue *q;
06556    char *option;
06557 
06558    if (ast_strlen_zero(data)) {
06559       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06560       return -1;
06561    }
06562 
06563    if ((option = strchr(data, ',')))
06564       *option++ = '\0';
06565    else
06566       option = "logged";
06567    if ((q = load_realtime_queue(data))) {
06568       ao2_lock(q);
06569       if (!strcasecmp(option, "logged")) {
06570          mem_iter = ao2_iterator_init(q->members, 0);
06571          while ((m = ao2_iterator_next(&mem_iter))) {
06572             /* Count the agents who are logged in and presently answering calls */
06573             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06574                count++;
06575             }
06576             ao2_ref(m, -1);
06577          }
06578          ao2_iterator_destroy(&mem_iter);
06579       } else if (!strcasecmp(option, "free")) {
06580          mem_iter = ao2_iterator_init(q->members, 0);
06581          while ((m = ao2_iterator_next(&mem_iter))) {
06582             /* Count the agents who are logged in and presently answering calls */
06583             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06584                count++;
06585             }
06586             ao2_ref(m, -1);
06587          }
06588          ao2_iterator_destroy(&mem_iter);
06589       } else if (!strcasecmp(option, "ready")) {
06590          time_t now;
06591          time(&now);
06592          mem_iter = ao2_iterator_init(q->members, 0);
06593          while ((m = ao2_iterator_next(&mem_iter))) {
06594             /* Count the agents who are logged in, not paused and not wrapping up */
06595             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06596                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06597                count++;
06598             }
06599             ao2_ref(m, -1);
06600          }
06601          ao2_iterator_destroy(&mem_iter);
06602       } else /* must be "count" */
06603          count = ao2_container_count(q->members);
06604       ao2_unlock(q);
06605       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06606    } else
06607       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06608 
06609    snprintf(buf, len, "%d", count);
06610 
06611    return 0;
06612 }

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

06620 {
06621    int count = 0;
06622    struct member *m;
06623    struct call_queue *q;
06624    struct ao2_iterator mem_iter;
06625    static int depflag = 1;
06626 
06627    if (depflag) {
06628       depflag = 0;
06629       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");
06630    }
06631 
06632    if (ast_strlen_zero(data)) {
06633       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06634       return -1;
06635    }
06636    
06637    if ((q = load_realtime_queue(data))) {
06638       ao2_lock(q);
06639       mem_iter = ao2_iterator_init(q->members, 0);
06640       while ((m = ao2_iterator_next(&mem_iter))) {
06641          /* Count the agents who are logged in and presently answering calls */
06642          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06643             count++;
06644          }
06645          ao2_ref(m, -1);
06646       }
06647       ao2_iterator_destroy(&mem_iter);
06648       ao2_unlock(q);
06649       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06650    } else
06651       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06652 
06653    snprintf(buf, len, "%d", count);
06654 
06655    return 0;
06656 }

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

06696 {
06697    struct call_queue *q, tmpq = {
06698       .name = data,  
06699    };
06700    struct member *m;
06701 
06702    /* Ensure an otherwise empty list doesn't return garbage */
06703    buf[0] = '\0';
06704 
06705    if (ast_strlen_zero(data)) {
06706       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06707       return -1;
06708    }
06709 
06710    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06711       int buflen = 0, count = 0;
06712       struct ao2_iterator mem_iter;
06713 
06714       ao2_lock(q);
06715       mem_iter = ao2_iterator_init(q->members, 0);
06716       while ((m = ao2_iterator_next(&mem_iter))) {
06717          /* strcat() is always faster than printf() */
06718          if (count++) {
06719             strncat(buf + buflen, ",", len - buflen - 1);
06720             buflen++;
06721          }
06722          strncat(buf + buflen, m->interface, len - buflen - 1);
06723          buflen += strlen(m->interface);
06724          /* Safeguard against overflow (negative length) */
06725          if (buflen >= len - 2) {
06726             ao2_ref(m, -1);
06727             ast_log(LOG_WARNING, "Truncating list\n");
06728             break;
06729          }
06730          ao2_ref(m, -1);
06731       }
06732       ao2_iterator_destroy(&mem_iter);
06733       ao2_unlock(q);
06734       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06735    } else
06736       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06737 
06738    /* We should already be terminated, but let's make sure. */
06739    buf[len - 1] = '\0';
06740 
06741    return 0;
06742 }

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

06660 {
06661    int count = 0;
06662    struct call_queue *q, tmpq = {
06663       .name = data,  
06664    };
06665    struct ast_variable *var = NULL;
06666 
06667    buf[0] = '\0';
06668    
06669    if (ast_strlen_zero(data)) {
06670       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06671       return -1;
06672    }
06673 
06674    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06675       ao2_lock(q);
06676       count = q->count;
06677       ao2_unlock(q);
06678       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06679    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06680       /* if the queue is realtime but was not found in memory, this
06681        * means that the queue had been deleted from memory since it was 
06682        * "dead." This means it has a 0 waiting count
06683        */
06684       count = 0;
06685       ast_variables_destroy(var);
06686    } else
06687       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06688 
06689    snprintf(buf, len, "%d", count);
06690 
06691    return 0;
06692 }

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

06480 {
06481    int res = -1;
06482    struct call_queue *q, tmpq = {
06483       .name = data,  
06484    };
06485 
06486    char interfacevar[256] = "";
06487    float sl = 0;
06488 
06489    if (ast_strlen_zero(data)) {
06490       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06491       return -1;
06492    }
06493 
06494    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06495       ao2_lock(q);
06496       if (q->setqueuevar) {
06497          sl = 0;
06498          res = 0;
06499 
06500          if (q->callscompleted > 0) {
06501             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06502          }
06503 
06504          snprintf(interfacevar, sizeof(interfacevar),
06505             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06506             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06507 
06508          pbx_builtin_setvar_multiple(chan, interfacevar);
06509       }
06510 
06511       ao2_unlock(q);
06512       queue_t_unref(q, "Done with QUEUE() function");
06513    } else {
06514       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06515    }
06516 
06517    snprintf(buf, len, "%d", res);
06518 
06519    return 0;
06520 }

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

Definition at line 1303 of file app_queue.c.

References ast_str_case_hash().

Referenced by load_module().

01304 {
01305    const struct call_queue *q = obj;
01306 
01307    return ast_str_case_hash(q->name);
01308 }

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

Definition at line 1321 of file app_queue.c.

References member::queuepos.

Referenced by queue_delme_members_decrement_followers(), and queue_member_follower_removal().

01322 {
01323    struct member *mem = obj;
01324    int *decrement_followers_after = arg;
01325 
01326    if (mem->queuepos > *decrement_followers_after) {
01327       mem->queuepos--;
01328    }
01329 
01330    return 0;
01331 }

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

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

01358 {
01359    int pos = mem->queuepos;
01360 
01361    /* 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
01362     * who would have been next otherwise. */
01363    if (pos < queue->rrpos) {
01364       queue->rrpos--;
01365    }
01366 
01367    ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &pos);
01368 }

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

Definition at line 1382 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01383 {
01384    ao2_ref(q, 1);
01385    return q;
01386 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06904 {
06905    const char *general_val = NULL;
06906    queue_persistent_members = 0;
06907    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06908       queue_persistent_members = ast_true(general_val);
06909    autofill_default = 0;
06910    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06911       autofill_default = ast_true(general_val);
06912    montype_default = 0;
06913    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06914       if (!strcasecmp(general_val, "mixmonitor"))
06915          montype_default = 1;
06916    }
06917    update_cdr = 0;
06918    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06919       update_cdr = ast_true(general_val);
06920    shared_lastcall = 0;
06921    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06922       shared_lastcall = ast_true(general_val);
06923 }

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

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

Referenced by find_queue_by_name_rt(), and reload_single_queue().

01999 {
02000    if (!strcasecmp(param, "musicclass") || 
02001       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
02002       ast_string_field_set(q, moh, val);
02003    } else if (!strcasecmp(param, "announce")) {
02004       ast_string_field_set(q, announce, val);
02005    } else if (!strcasecmp(param, "context")) {
02006       ast_string_field_set(q, context, val);
02007    } else if (!strcasecmp(param, "timeout")) {
02008       q->timeout = atoi(val);
02009       if (q->timeout < 0)
02010          q->timeout = DEFAULT_TIMEOUT;
02011    } else if (!strcasecmp(param, "ringinuse")) {
02012       q->ringinuse = ast_true(val);
02013    } else if (!strcasecmp(param, "setinterfacevar")) {
02014       q->setinterfacevar = ast_true(val);
02015    } else if (!strcasecmp(param, "setqueuevar")) {
02016       q->setqueuevar = ast_true(val);
02017    } else if (!strcasecmp(param, "setqueueentryvar")) {
02018       q->setqueueentryvar = ast_true(val);
02019    } else if (!strcasecmp(param, "monitor-format")) {
02020       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
02021    } else if (!strcasecmp(param, "membermacro")) {
02022       ast_string_field_set(q, membermacro, val);
02023    } else if (!strcasecmp(param, "membergosub")) {
02024       ast_string_field_set(q, membergosub, val);
02025    } else if (!strcasecmp(param, "queue-youarenext")) {
02026       ast_string_field_set(q, sound_next, val);
02027    } else if (!strcasecmp(param, "queue-thereare")) {
02028       ast_string_field_set(q, sound_thereare, val);
02029    } else if (!strcasecmp(param, "queue-callswaiting")) {
02030       ast_string_field_set(q, sound_calls, val);
02031    } else if (!strcasecmp(param, "queue-quantity1")) {
02032       ast_string_field_set(q, queue_quantity1, val);
02033    } else if (!strcasecmp(param, "queue-quantity2")) {
02034       ast_string_field_set(q, queue_quantity2, val);
02035    } else if (!strcasecmp(param, "queue-holdtime")) {
02036       ast_string_field_set(q, sound_holdtime, val);
02037    } else if (!strcasecmp(param, "queue-minutes")) {
02038       ast_string_field_set(q, sound_minutes, val);
02039    } else if (!strcasecmp(param, "queue-minute")) {
02040       ast_string_field_set(q, sound_minute, val);
02041    } else if (!strcasecmp(param, "queue-seconds")) {
02042       ast_string_field_set(q, sound_seconds, val);
02043    } else if (!strcasecmp(param, "queue-thankyou")) {
02044       ast_string_field_set(q, sound_thanks, val);
02045    } else if (!strcasecmp(param, "queue-callerannounce")) {
02046       ast_string_field_set(q, sound_callerannounce, val);
02047    } else if (!strcasecmp(param, "queue-reporthold")) {
02048       ast_string_field_set(q, sound_reporthold, val);
02049    } else if (!strcasecmp(param, "announce-frequency")) {
02050       q->announcefrequency = atoi(val);
02051    } else if (!strcasecmp(param, "announce-to-first-user")) {
02052       q->announce_to_first_user = ast_true(val);
02053    } else if (!strcasecmp(param, "min-announce-frequency")) {
02054       q->minannouncefrequency = atoi(val);
02055       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
02056    } else if (!strcasecmp(param, "announce-round-seconds")) {
02057       q->roundingseconds = atoi(val);
02058       /* Rounding to any other values just doesn't make sense... */
02059       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
02060          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
02061          if (linenum >= 0) {
02062             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
02063                "using 0 instead for queue '%s' at line %d of queues.conf\n",
02064                val, param, q->name, linenum);
02065          } else {
02066             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
02067                "using 0 instead for queue '%s'\n", val, param, q->name);
02068          }
02069          q->roundingseconds=0;
02070       }
02071    } else if (!strcasecmp(param, "announce-holdtime")) {
02072       if (!strcasecmp(val, "once"))
02073          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
02074       else if (ast_true(val))
02075          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
02076       else
02077          q->announceholdtime = 0;
02078    } else if (!strcasecmp(param, "announce-position")) {
02079       if (!strcasecmp(val, "limit"))
02080          q->announceposition = ANNOUNCEPOSITION_LIMIT;
02081       else if (!strcasecmp(val, "more"))
02082          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
02083       else if (ast_true(val))
02084          q->announceposition = ANNOUNCEPOSITION_YES;
02085       else
02086          q->announceposition = ANNOUNCEPOSITION_NO;
02087    } else if (!strcasecmp(param, "announce-position-limit")) {
02088       q->announcepositionlimit = atoi(val);
02089    } else if (!strcasecmp(param, "periodic-announce")) {
02090       if (strchr(val, ',')) {
02091          char *s, *buf = ast_strdupa(val);
02092          unsigned int i = 0;
02093 
02094          while ((s = strsep(&buf, ",|"))) {
02095             if (!q->sound_periodicannounce[i])
02096                q->sound_periodicannounce[i] = ast_str_create(16);
02097             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
02098             i++;
02099             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
02100                break;
02101          }
02102          q->numperiodicannounce = i;
02103       } else {
02104          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
02105          q->numperiodicannounce = 1;
02106       }
02107    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
02108       q->periodicannouncefrequency = atoi(val);
02109    } else if (!strcasecmp(param, "relative-periodic-announce")) {
02110       q->relativeperiodicannounce = ast_true(val);
02111    } else if (!strcasecmp(param, "random-periodic-announce")) {
02112       q->randomperiodicannounce = ast_true(val);
02113    } else if (!strcasecmp(param, "retry")) {
02114       q->retry = atoi(val);
02115       if (q->retry <= 0)
02116          q->retry = DEFAULT_RETRY;
02117    } else if (!strcasecmp(param, "wrapuptime")) {
02118       q->wrapuptime = atoi(val);
02119    } else if (!strcasecmp(param, "penaltymemberslimit")) {
02120       if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
02121          q->penaltymemberslimit = 0;
02122       }
02123    } else if (!strcasecmp(param, "autofill")) {
02124       q->autofill = ast_true(val);
02125    } else if (!strcasecmp(param, "monitor-type")) {
02126       if (!strcasecmp(val, "mixmonitor"))
02127          q->montype = 1;
02128    } else if (!strcasecmp(param, "autopause")) {
02129       q->autopause = autopause2int(val);
02130    } else if (!strcasecmp(param, "maxlen")) {
02131       q->maxlen = atoi(val);
02132       if (q->maxlen < 0)
02133          q->maxlen = 0;
02134    } else if (!strcasecmp(param, "servicelevel")) {
02135       q->servicelevel= atoi(val);
02136    } else if (!strcasecmp(param, "strategy")) {
02137       int strategy;
02138 
02139       /* We are a static queue and already have set this, no need to do it again */
02140       if (failunknown) {
02141          return;
02142       }
02143       strategy = strat2int(val);
02144       if (strategy < 0) {
02145          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02146             val, q->name);
02147          q->strategy = QUEUE_STRATEGY_RINGALL;
02148       }
02149       if (strategy == q->strategy) {
02150          return;
02151       }
02152       if (strategy == QUEUE_STRATEGY_LINEAR) {
02153          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
02154          return;
02155       }
02156       q->strategy = strategy;
02157    } else if (!strcasecmp(param, "joinempty")) {
02158       parse_empty_options(val, &q->joinempty, 1);
02159    } else if (!strcasecmp(param, "leavewhenempty")) {
02160       parse_empty_options(val, &q->leavewhenempty, 0);
02161    } else if (!strcasecmp(param, "eventmemberstatus")) {
02162       q->maskmemberstatus = !ast_true(val);
02163    } else if (!strcasecmp(param, "eventwhencalled")) {
02164       if (!strcasecmp(val, "vars")) {
02165          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
02166       } else {
02167          q->eventwhencalled = ast_true(val) ? 1 : 0;
02168       }
02169    } else if (!strcasecmp(param, "reportholdtime")) {
02170       q->reportholdtime = ast_true(val);
02171    } else if (!strcasecmp(param, "memberdelay")) {
02172       q->memberdelay = atoi(val);
02173    } else if (!strcasecmp(param, "weight")) {
02174       q->weight = atoi(val);
02175    } else if (!strcasecmp(param, "timeoutrestart")) {
02176       q->timeoutrestart = ast_true(val);
02177    } else if (!strcasecmp(param, "defaultrule")) {
02178       ast_string_field_set(q, defaultrule, val);
02179    } else if (!strcasecmp(param, "timeoutpriority")) {
02180       if (!strcasecmp(val, "conf")) {
02181          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
02182       } else {
02183          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
02184       }
02185    } else if (failunknown) {
02186       if (linenum >= 0) {
02187          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
02188             q->name, param, linenum);
02189       } else {
02190          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
02191       }
02192    }
02193 }

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

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

07560 {
07561    switch ( cmd ) {
07562    case CLI_INIT:
07563       e->command = "queue show";
07564       e->usage =
07565          "Usage: queue show\n"
07566          "       Provides summary information on a specified queue.\n";
07567       return NULL;
07568    case CLI_GENERATE:
07569       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07570    }
07571 
07572    return __queues_show(NULL, a->fd, a->argc, a->argv);
07573 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4535 of file app_queue.c.

References ast_free.

04536 {
04537    struct queue_transfer_ds *qtds = data;
04538    ast_free(qtds);
04539 }

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

04559 {
04560    struct queue_transfer_ds *qtds = data;
04561    struct queue_ent *qe = qtds->qe;
04562    struct member *member = qtds->member;
04563    time_t callstart = qtds->starttime;
04564    int callcompletedinsl = qtds->callcompletedinsl;
04565    struct ast_datastore *datastore;
04566 
04567    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04568             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04569             (long) (time(NULL) - callstart), qe->opos);
04570 
04571    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04572    
04573    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04574    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04575       ast_channel_datastore_remove(old_chan, datastore);
04576       /* Datastore is freed in try_calling() */
04577    } else {
04578       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04579    }
04580 }

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

Definition at line 1388 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec(), and queues_data_provider_get().

01389 {
01390    ao2_ref(q, -1);
01391    return NULL;
01392 }

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

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

08730 {
08731    struct ao2_iterator i;
08732    struct call_queue *queue, *queue_realtime = NULL;
08733    struct ast_config *cfg;
08734    char *queuename;
08735 
08736    /* load realtime queues. */
08737    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08738    if (cfg) {
08739       for (queuename = ast_category_browse(cfg, NULL);
08740             !ast_strlen_zero(queuename);
08741             queuename = ast_category_browse(cfg, queuename)) {
08742          if ((queue = load_realtime_queue(queuename))) {
08743             queue_unref(queue);
08744          }
08745       }
08746       ast_config_destroy(cfg);
08747    }
08748 
08749    /* static queues. */
08750    i = ao2_iterator_init(queues, 0);
08751    while ((queue = ao2_iterator_next(&i))) {
08752       ao2_lock(queue);
08753       if (queue->realtime) {
08754          queue_realtime = load_realtime_queue(queue->name);
08755          if (!queue_realtime) {
08756             ao2_unlock(queue);
08757             queue_unref(queue);
08758             continue;
08759          }
08760          queue_unref(queue_realtime);
08761       }
08762 
08763       queues_data_provider_get_helper(search, data_root, queue);
08764       ao2_unlock(queue);
08765       queue_unref(queue);
08766    }
08767    ao2_iterator_destroy(&i);
08768 
08769    return 0;
08770 }

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

08624 {
08625    struct ao2_iterator im;
08626    struct member *member;
08627    struct queue_ent *qe;
08628    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08629    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08630 
08631    data_queue = ast_data_add_node(data_root, "queue");
08632    if (!data_queue) {
08633       return;
08634    }
08635 
08636    ast_data_add_structure(call_queue, data_queue, queue);
08637 
08638    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08639    ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08640 
08641    /* announce position */
08642    enum_node = ast_data_add_node(data_queue, "announceposition");
08643    if (!enum_node) {
08644       return;
08645    }
08646    switch (queue->announceposition) {
08647    case ANNOUNCEPOSITION_LIMIT:
08648       ast_data_add_str(enum_node, "text", "limit");
08649       break;
08650    case ANNOUNCEPOSITION_MORE_THAN:
08651       ast_data_add_str(enum_node, "text", "more");
08652       break;
08653    case ANNOUNCEPOSITION_YES:
08654       ast_data_add_str(enum_node, "text", "yes");
08655       break;
08656    case ANNOUNCEPOSITION_NO:
08657       ast_data_add_str(enum_node, "text", "no");
08658       break;
08659    default:
08660       ast_data_add_str(enum_node, "text", "unknown");
08661       break;
08662    }
08663    ast_data_add_int(enum_node, "value", queue->announceposition);
08664 
08665    /* add queue members */
08666    im = ao2_iterator_init(queue->members, 0);
08667    while ((member = ao2_iterator_next(&im))) {
08668       if (!data_members) {
08669          data_members = ast_data_add_node(data_queue, "members");
08670          if (!data_members) {
08671             ao2_ref(member, -1);
08672             continue;
08673          }
08674       }
08675 
08676       data_member = ast_data_add_node(data_members, "member");
08677       if (!data_member) {
08678          ao2_ref(member, -1);
08679          continue;
08680       }
08681 
08682       ast_data_add_structure(member, data_member, member);
08683 
08684       ao2_ref(member, -1);
08685    }
08686    ao2_iterator_destroy(&im);
08687 
08688    /* include the callers inside the result. */
08689    if (queue->head) {
08690       for (qe = queue->head; qe; qe = qe->next) {
08691          if (!data_callers) {
08692             data_callers = ast_data_add_node(data_queue, "callers");
08693             if (!data_callers) {
08694                continue;
08695             }
08696          }
08697 
08698          data_caller = ast_data_add_node(data_callers, "caller");
08699          if (!data_caller) {
08700             continue;
08701          }
08702 
08703          ast_data_add_structure(queue_ent, data_caller, qe);
08704 
08705          /* add the caller channel. */
08706          data_caller_channel = ast_data_add_node(data_caller, "channel");
08707          if (!data_caller_channel) {
08708             continue;
08709          }
08710 
08711          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08712       }
08713    }
08714 
08715    /* if this queue doesn't match remove the added queue. */
08716    if (!ast_data_search_match(search, data_queue)) {
08717       ast_data_remove_node(data_root, data_queue);
08718    }
08719 }

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

Definition at line 2894 of file app_queue.c.

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

Referenced by try_calling().

02895 {
02896    int oldvalue;
02897 
02898    /* Calculate holdtime using an exponential average */
02899    /* Thanks to SRT for this contribution */
02900    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02901 
02902    ao2_lock(qe->parent);
02903    oldvalue = qe->parent->holdtime;
02904    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02905    ao2_unlock(qe->parent);
02906 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

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

03586 {
03587    set_queue_variables(qe->parent, qe->chan);
03588    ao2_lock(qe->parent);
03589    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03590       "Queue: %s\r\n"
03591       "Uniqueid: %s\r\n"
03592       "Position: %d\r\n"
03593       "OriginalPosition: %d\r\n"
03594       "HoldTime: %d\r\n",
03595       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03596 
03597    qe->parent->callsabandoned++;
03598    ao2_unlock(qe->parent);
03599 }

static int reload ( void   )  [static]

Definition at line 8904 of file app_queue.c.

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

08905 {
08906    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08907    ast_unload_realtime("queue_members");
08908    reload_handler(1, &mask, NULL);
08909    return 0;
08910 }

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

07280 {
07281    int res = 0;
07282 
07283    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
07284       res |= reload_queue_rules(reload);
07285    }
07286    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
07287       res |= clear_stats(queuename);
07288    }
07289    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
07290       res |= reload_queues(reload, mask, queuename);
07291    }
07292    return res;
07293 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

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

05821 {
05822    char *cur_ptr;
05823    const char *queue_name;
05824    char *member;
05825    char *interface;
05826    char *membername = NULL;
05827    char *state_interface;
05828    char *penalty_tok;
05829    int penalty = 0;
05830    char *paused_tok;
05831    int paused = 0;
05832    struct ast_db_entry *db_tree;
05833    struct ast_db_entry *entry;
05834    struct call_queue *cur_queue;
05835    char *queue_data;
05836 
05837    /* Each key in 'pm_family' is the name of a queue */
05838    db_tree = ast_db_gettree(pm_family, NULL);
05839    for (entry = db_tree; entry; entry = entry->next) {
05840 
05841       queue_name = entry->key + strlen(pm_family) + 2;
05842 
05843       {
05844          struct call_queue tmpq = {
05845             .name = queue_name,
05846          };
05847          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05848       }  
05849 
05850       if (!cur_queue)
05851          cur_queue = load_realtime_queue(queue_name);
05852 
05853       if (!cur_queue) {
05854          /* If the queue no longer exists, remove it from the
05855           * database */
05856          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05857          ast_db_del(pm_family, queue_name);
05858          continue;
05859       } 
05860 
05861       if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) {
05862          queue_t_unref(cur_queue, "Expire reload reference");
05863          continue;
05864       }
05865 
05866       cur_ptr = queue_data;
05867       while ((member = strsep(&cur_ptr, ",|"))) {
05868          if (ast_strlen_zero(member))
05869             continue;
05870 
05871          interface = strsep(&member, ";");
05872          penalty_tok = strsep(&member, ";");
05873          paused_tok = strsep(&member, ";");
05874          membername = strsep(&member, ";");
05875          state_interface = strsep(&member, ";");
05876 
05877          if (!penalty_tok) {
05878             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05879             break;
05880          }
05881          penalty = strtol(penalty_tok, NULL, 10);
05882          if (errno == ERANGE) {
05883             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05884             break;
05885          }
05886          
05887          if (!paused_tok) {
05888             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05889             break;
05890          }
05891          paused = strtol(paused_tok, NULL, 10);
05892          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05893             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05894             break;
05895          }
05896 
05897          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05898          
05899          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05900             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05901             break;
05902          }
05903       }
05904       queue_t_unref(cur_queue, "Expire reload reference");
05905       ast_free(queue_data);
05906    }
05907 
05908    if (db_tree) {
05909       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05910       ast_db_freetree(db_tree);
05911    }
05912 }

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

06855 {
06856    struct ast_config *cfg;
06857    struct rule_list *rl_iter, *new_rl;
06858    struct penalty_rule *pr_iter;
06859    char *rulecat = NULL;
06860    struct ast_variable *rulevar = NULL;
06861    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06862    
06863    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06864       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06865       return AST_MODULE_LOAD_SUCCESS;
06866    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06867       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06868       return AST_MODULE_LOAD_SUCCESS;
06869    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06870       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
06871       return AST_MODULE_LOAD_SUCCESS;
06872    }
06873 
06874    AST_LIST_LOCK(&rule_lists);
06875    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06876       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06877          ast_free(pr_iter);
06878       ast_free(rl_iter);
06879    }
06880    while ((rulecat = ast_category_browse(cfg, rulecat))) {
06881       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06882          AST_LIST_UNLOCK(&rule_lists);
06883          ast_config_destroy(cfg);
06884          return AST_MODULE_LOAD_FAILURE;
06885       } else {
06886          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06887          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06888          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06889             if(!strcasecmp(rulevar->name, "penaltychange"))
06890                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06891             else
06892                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06893       }
06894    }
06895    AST_LIST_UNLOCK(&rule_lists);
06896 
06897    ast_config_destroy(cfg);
06898 
06899    return AST_MODULE_LOAD_SUCCESS;
06900 }

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

07185 {
07186    struct ast_config *cfg;
07187    char *cat;
07188    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
07189    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
07190    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
07191 
07192    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
07193       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
07194       return -1;
07195    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
07196       return 0;
07197    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
07198       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
07199       return -1;
07200    }
07201 
07202    /* We've made it here, so it looks like we're doing operations on all queues. */
07203    ao2_lock(queues);
07204 
07205    /* Mark all queues as dead for the moment if we're reloading queues.
07206     * For clarity, we could just be reloading members, in which case we don't want to mess
07207     * with the other queue parameters at all*/
07208    if (queue_reload) {
07209       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
07210    }
07211 
07212    if (member_reload) {
07213       ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename);
07214    }
07215 
07216    /* Chug through config file */
07217    cat = NULL;
07218    while ((cat = ast_category_browse(cfg, cat)) ) {
07219       if (!strcasecmp(cat, "general") && queue_reload) {
07220          queue_set_global_params(cfg);
07221          continue;
07222       }
07223       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
07224          reload_single_queue(cfg, mask, cat);
07225    }
07226 
07227    ast_config_destroy(cfg);
07228    /* Unref all the dead queues if we were reloading queues */
07229    if (queue_reload) {
07230       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
07231    }
07232    ao2_unlock(queues);
07233    return 0;
07234 }

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

06934 {
06935    char *membername, *interface, *state_interface, *tmp;
06936    char *parse;
06937    struct member *cur, *newm;
06938    struct member tmpmem;
06939    int penalty;
06940    AST_DECLARE_APP_ARGS(args,
06941       AST_APP_ARG(interface);
06942       AST_APP_ARG(penalty);
06943       AST_APP_ARG(membername);
06944       AST_APP_ARG(state_interface);
06945    );
06946 
06947    if (ast_strlen_zero(memberdata)) {
06948       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06949       return;
06950    }
06951 
06952    /* Add a new member */
06953    parse = ast_strdupa(memberdata);
06954             
06955    AST_STANDARD_APP_ARGS(args, parse);
06956 
06957    interface = args.interface;
06958    if (!ast_strlen_zero(args.penalty)) {
06959       tmp = args.penalty;
06960       ast_strip(tmp);
06961       penalty = atoi(tmp);
06962       if (penalty < 0) {
06963          penalty = 0;
06964       }
06965    } else {
06966       penalty = 0;
06967    }
06968 
06969    if (!ast_strlen_zero(args.membername)) {
06970       membername = args.membername;
06971       ast_strip(membername);
06972    } else {
06973       membername = interface;
06974    }
06975 
06976    if (!ast_strlen_zero(args.state_interface)) {
06977       state_interface = args.state_interface;
06978       ast_strip(state_interface);
06979    } else {
06980       state_interface = interface;
06981    }
06982 
06983    /* Find the old position in the list */
06984    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06985    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER);
06986 
06987    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06988       if (cur) {
06989          /* Round Robin Queue Position must be copied if this is replacing an existing member */
06990          ao2_lock(q->members);
06991          newm->queuepos = cur->queuepos;
06992          ao2_link(q->members, newm);
06993          ao2_unlink(q->members, cur);
06994          ao2_unlock(q->members);
06995       } else {
06996          /* Otherwise we need to add using the function that will apply a round robin queue position manually. */
06997          member_add_to_queue(q, newm);
06998       }
06999       ao2_ref(newm, -1);
07000    }
07001    newm = NULL;
07002 
07003    if (cur) {
07004       ao2_ref(cur, -1);
07005    }
07006 }

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

07041 {
07042    int new;
07043    struct call_queue *q = NULL;
07044    /*We're defining a queue*/
07045    struct call_queue tmpq = {
07046       .name = queuename,
07047    };
07048    const char *tmpvar;
07049    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
07050    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
07051    int prev_weight = 0;
07052    struct ast_variable *var;
07053    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
07054       if (queue_reload) {
07055          /* Make one then */
07056          if (!(q = alloc_queue(queuename))) {
07057             return;
07058          }
07059       } else {
07060          /* Since we're not reloading queues, this means that we found a queue
07061           * in the configuration file which we don't know about yet. Just return.
07062           */
07063          return;
07064       }
07065       new = 1;
07066    } else {
07067       new = 0;
07068    }
07069    
07070    if (!new) {
07071       ao2_lock(q);
07072       prev_weight = q->weight ? 1 : 0;
07073    }
07074    /* Check if we already found a queue with this name in the config file */
07075    if (q->found) {
07076       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
07077       if (!new) {
07078          /* It should be impossible to *not* hit this case*/
07079          ao2_unlock(q);
07080       }
07081       queue_t_unref(q, "We exist! Expiring temporary pointer");
07082       return;
07083    }
07084    /* Due to the fact that the "linear" strategy will have a different allocation
07085     * scheme for queue members, we must devise the queue's strategy before other initializations.
07086     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
07087     * container used will have only a single bucket instead of the typical number.
07088     */
07089    if (queue_reload) {
07090       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
07091          q->strategy = strat2int(tmpvar);
07092          if (q->strategy < 0) {
07093             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
07094             tmpvar, q->name);
07095             q->strategy = QUEUE_STRATEGY_RINGALL;
07096          }
07097       } else {
07098          q->strategy = QUEUE_STRATEGY_RINGALL;
07099       }
07100       init_queue(q);
07101    }
07102    if (member_reload) {
07103       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
07104       q->found = 1;
07105    }
07106    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
07107       if (member_reload && !strcasecmp(var->name, "member")) {
07108          reload_single_member(var->value, q);
07109       } else if (queue_reload) {
07110          queue_set_param(q, var->name, var->value, var->lineno, 1);
07111       }
07112    }
07113    /* At this point, we've determined if the queue has a weight, so update use_weight
07114     * as appropriate
07115     */
07116    if (!q->weight && prev_weight) {
07117       ast_atomic_fetchadd_int(&use_weight, -1);
07118    }
07119    else if (q->weight && !prev_weight) {
07120       ast_atomic_fetchadd_int(&use_weight, +1);
07121    }
07122 
07123    /* Free remaining members marked as delme */
07124    if (member_reload) {
07125       ao2_lock(q->members);
07126       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q);
07127       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
07128       ao2_unlock(q->members);
07129    }
07130 
07131    if (new) {
07132       queues_t_link(queues, q, "Add queue to container");
07133    } else {
07134       ao2_unlock(q);
07135    }
07136    queue_t_unref(q, "Expiring creation reference");
07137 }

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

05564 {
05565    struct call_queue *q, tmpq = {
05566       .name = queuename,   
05567    };
05568    struct member *mem, tmpmem;
05569    int res = RES_NOSUCHQUEUE;
05570 
05571    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05572    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05573       ao2_lock(q);
05574       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05575          /* XXX future changes should beware of this assumption!! */
05576          if (!mem->dynamic) {
05577             ao2_ref(mem, -1);
05578             ao2_unlock(q);
05579             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05580             return RES_NOT_DYNAMIC;
05581          }
05582          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05583             "Queue: %s\r\n"
05584             "Location: %s\r\n"
05585             "MemberName: %s\r\n",
05586             q->name, mem->interface, mem->membername);
05587          member_remove_from_queue(q, mem);
05588          ao2_ref(mem, -1);
05589 
05590          if (queue_persistent_members)
05591             dump_queue_members(q);
05592          
05593          res = RES_OKAY;
05594       } else {
05595          res = RES_EXISTS;
05596       }
05597       ao2_unlock(q);
05598       queue_t_unref(q, "Expiring temporary reference");
05599    }
05600 
05601    return res;
05602 }

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

Definition at line 7139 of file app_queue.c.

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

Referenced by reload_queues().

07140 {
07141    struct call_queue *q = obj;
07142    char *queuename = arg;
07143    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
07144       q->found = 0;
07145 
07146    }
07147    return 0;
07148 }

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

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

Referenced by ring_one().

03262 {
03263    int res;
03264    int status;
03265    char tech[256];
03266    char *location;
03267    const char *macrocontext, *macroexten;
03268 
03269    /* on entry here, we know that tmp->chan == NULL */
03270    if (!can_ring_entry(qe, tmp)) {
03271       if (qe->chan->cdr) {
03272          ast_cdr_busy(qe->chan->cdr);
03273       }
03274       tmp->stillgoing = 0;
03275       ++*busies;
03276       return 0;
03277    }
03278    ast_assert(qe->parent->ringinuse || tmp->member->call_pending);
03279 
03280    ast_copy_string(tech, tmp->interface, sizeof(tech));
03281    if ((location = strchr(tech, '/')))
03282       *location++ = '\0';
03283    else
03284       location = "";
03285 
03286    /* Request the peer */
03287    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03288    if (!tmp->chan) {       /* If we can't, just go on to the next call */
03289       ao2_lock(qe->parent);
03290       qe->parent->rrpos++;
03291       qe->linpos++;
03292       ao2_unlock(qe->parent);
03293 
03294       member_call_pending_clear(tmp->member);
03295 
03296       if (qe->chan->cdr) {
03297          ast_cdr_busy(qe->chan->cdr);
03298       }
03299       tmp->stillgoing = 0;
03300       ++*busies;
03301       return 0;
03302    }
03303 
03304    ast_channel_lock_both(tmp->chan, qe->chan);
03305 
03306    if (qe->cancel_answered_elsewhere) {
03307       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03308    }
03309    tmp->chan->appl = "AppQueue";
03310    tmp->chan->data = "(Outgoing Line)";
03311    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03312 
03313    /* If the new channel has no callerid, try to guess what it should be */
03314    if (!tmp->chan->caller.id.number.valid) {
03315       if (qe->chan->connected.id.number.valid) {
03316          struct ast_party_caller caller;
03317 
03318          ast_party_caller_set_init(&caller, &tmp->chan->caller);
03319          caller.id = qe->chan->connected.id;
03320          caller.ani = qe->chan->connected.ani;
03321          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03322       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03323          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03324       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03325          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
03326       }
03327       tmp->dial_callerid_absent = 1;
03328    }
03329 
03330    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03331 
03332    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03333 
03334    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03335 
03336    /* Inherit specially named variables from parent channel */
03337    ast_channel_inherit_variables(qe->chan, tmp->chan);
03338    ast_channel_datastore_inherit(qe->chan, tmp->chan);
03339 
03340    /* Presense of ADSI CPE on outgoing channel follows ours */
03341    tmp->chan->adsicpe = qe->chan->adsicpe;
03342 
03343    /* Inherit context and extension */
03344    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03345    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03346    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03347    if (!ast_strlen_zero(macroexten))
03348       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03349    else
03350       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03351    if (ast_cdr_isset_unanswered()) {
03352       /* they want to see the unanswered dial attempts! */
03353       /* set up the CDR fields on all the CDRs to give sensical information */
03354       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
03355       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03356       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03357       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03358       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03359       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03360       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03361       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03362       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03363       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03364       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03365    }
03366 
03367    ast_channel_unlock(tmp->chan);
03368    ast_channel_unlock(qe->chan);
03369 
03370    /* Place the call, but don't wait on the answer */
03371    if ((res = ast_call(tmp->chan, location, 0))) {
03372       /* Again, keep going even if there's an error */
03373       ast_verb(3, "Couldn't call %s\n", tmp->interface);
03374       do_hang(tmp);
03375       member_call_pending_clear(tmp->member);
03376       ++*busies;
03377       return 0;
03378    }
03379 
03380    if (qe->parent->eventwhencalled) {
03381       char vars[2048];
03382 
03383       ast_channel_lock_both(tmp->chan, qe->chan);
03384 
03385       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03386          "Queue: %s\r\n"
03387          "AgentCalled: %s\r\n"
03388          "AgentName: %s\r\n"
03389          "ChannelCalling: %s\r\n"
03390          "DestinationChannel: %s\r\n"
03391          "CallerIDNum: %s\r\n"
03392          "CallerIDName: %s\r\n"
03393          "ConnectedLineNum: %s\r\n"
03394          "ConnectedLineName: %s\r\n"
03395          "Context: %s\r\n"
03396          "Extension: %s\r\n"
03397          "Priority: %d\r\n"
03398          "Uniqueid: %s\r\n"
03399          "%s",
03400          qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03401          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
03402          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
03403          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
03404          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
03405          qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03406          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03407 
03408       ast_channel_unlock(tmp->chan);
03409       ast_channel_unlock(qe->chan);
03410 
03411       ast_verb(3, "Called %s\n", tmp->interface);
03412    }
03413 
03414    member_call_pending_clear(tmp->member);
03415    return 1;
03416 }

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

03445 {
03446    int ret = 0;
03447 
03448    while (ret == 0) {
03449       struct callattempt *best = find_best(outgoing);
03450       if (!best) {
03451          ast_debug(1, "Nobody left to try ringing in queue\n");
03452          break;
03453       }
03454       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03455          struct callattempt *cur;
03456          /* Ring everyone who shares this best metric (for ringall) */
03457          for (cur = outgoing; cur; cur = cur->q_next) {
03458             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03459                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03460                ret |= ring_entry(qe, cur, busies);
03461             }
03462          }
03463       } else {
03464          /* Ring just the best channel */
03465          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03466          ret = ring_entry(qe, best, busies);
03467       }
03468       
03469       /* If we have timed out, break out */
03470       if (qe->expire && (time(NULL) >= qe->expire)) {
03471          ast_debug(1, "Queue timed out while ringing members.\n");
03472          ret = 0;
03473          break;
03474       }
03475    }
03476 
03477    return ret;
03478 }

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

03603 {
03604    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03605 
03606    /* Stop ringing, and resume MOH if specified */
03607    if (qe->ring_when_ringing) {
03608       ast_indicate(qe->chan, -1);
03609       ast_moh_start(qe->chan, qe->moh, NULL);
03610    }
03611 
03612    if (qe->parent->eventwhencalled) {
03613       char vars[2048];
03614 
03615       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03616                   "Queue: %s\r\n"
03617                   "Uniqueid: %s\r\n"
03618                   "Channel: %s\r\n"
03619                   "Member: %s\r\n"
03620                   "MemberName: %s\r\n"
03621                   "Ringtime: %d\r\n"
03622                   "%s",
03623                   qe->parent->name,
03624                   qe->chan->uniqueid,
03625                   qe->chan->name,
03626                   interface,
03627                   membername,
03628                   rnatime,
03629                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03630    }
03631    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03632    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03633       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03634          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03635             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03636                interface, qe->parent->name);
03637          } else {
03638             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03639          }
03640       } else {
03641          /* If queue autopause is mode all, just don't send any queue to stop.
03642          * the function will stop in all queues */
03643          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03644             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03645                   interface, qe->parent->name);
03646          } else {
03647                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03648          }
03649       }
03650    }
03651    return;
03652 }

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

RemoveQueueMember application.

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

05988 {
05989    int res=-1;
05990    char *parse, *temppos = NULL;
05991    AST_DECLARE_APP_ARGS(args,
05992       AST_APP_ARG(queuename);
05993       AST_APP_ARG(interface);
05994    );
05995 
05996 
05997    if (ast_strlen_zero(data)) {
05998       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n");
05999       return -1;
06000    }
06001 
06002    parse = ast_strdupa(data);
06003 
06004    AST_STANDARD_APP_ARGS(args, parse);
06005 
06006    if (ast_strlen_zero(args.interface)) {
06007       args.interface = ast_strdupa(chan->name);
06008       temppos = strrchr(args.interface, '-');
06009       if (temppos)
06010          *temppos = '\0';
06011    }
06012 
06013    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
06014 
06015    switch (remove_from_queue(args.queuename, args.interface)) {
06016    case RES_OKAY:
06017       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
06018       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
06019       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
06020       res = 0;
06021       break;
06022    case RES_EXISTS:
06023       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
06024       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
06025       res = 0;
06026       break;
06027    case RES_NOSUCHQUEUE:
06028       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
06029       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
06030       res = 0;
06031       break;
06032    case RES_NOT_DYNAMIC:
06033       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
06034       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
06035       res = 0;
06036       break;
06037    }
06038 
06039    return res;
06040 }

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

02230 {
02231    struct member *m;
02232    struct ao2_iterator mem_iter;
02233    int penalty = 0;
02234    int paused  = 0;
02235    int found = 0;
02236 
02237    if (ast_strlen_zero(rt_uniqueid)) {
02238       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02239       return;
02240    }
02241 
02242    if (penalty_str) {
02243       penalty = atoi(penalty_str);
02244       if (penalty < 0)
02245          penalty = 0;
02246    }
02247 
02248    if (paused_str) {
02249       paused = atoi(paused_str);
02250       if (paused < 0)
02251          paused = 0;
02252    }
02253 
02254    /* Find member by realtime uniqueid and update */
02255    mem_iter = ao2_iterator_init(q->members, 0);
02256    while ((m = ao2_iterator_next(&mem_iter))) {
02257       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02258          m->dead = 0;   /* Do not delete this one. */
02259          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02260          if (paused_str)
02261             m->paused = paused;
02262          if (strcasecmp(state_interface, m->state_interface)) {
02263             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02264          }     
02265          m->penalty = penalty;
02266          found = 1;
02267          ao2_ref(m, -1);
02268          break;
02269       }
02270       ao2_ref(m, -1);
02271    }
02272    ao2_iterator_destroy(&mem_iter);
02273 
02274    /* Create a new member */
02275    if (!found) {
02276       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02277          m->dead = 0;
02278          m->realtime = 1;
02279          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02280          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02281          member_add_to_queue(q, m);
02282          ao2_ref(m, -1);
02283          m = NULL;
02284       }
02285    }
02286 }

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

Playback announcement to queued members if period has elapsed.

Definition at line 3529 of file app_queue.c.

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

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

03530 {
03531    int res = 0;
03532    time_t now;
03533 
03534    /* Get the current time */
03535    time(&now);
03536 
03537    /* Check to see if it is time to announce */
03538    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03539       return 0;
03540 
03541    /* Stop the music on hold so we can play our own file */
03542    if (ringing)
03543       ast_indicate(qe->chan,-1);
03544    else
03545       ast_moh_stop(qe->chan);
03546 
03547    ast_verb(3, "Playing periodic announcement\n");
03548    
03549    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03550       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03551    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03552       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03553       qe->last_periodic_announce_sound = 0;
03554    }
03555    
03556    /* play the announcement */
03557    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03558 
03559    if (res > 0 && !valid_exit(qe, res))
03560       res = 0;
03561 
03562    /* Resume Music on Hold if the caller is going to stay in the queue */
03563    if (!res) {
03564       if (ringing)
03565          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03566       else
03567          ast_moh_start(qe->chan, qe->moh, NULL);
03568    }
03569 
03570    /* update last_periodic_announce_time */
03571    if (qe->parent->relativeperiodicannounce)
03572       time(&qe->last_periodic_announce_time);
03573    else
03574       qe->last_periodic_announce_time = now;
03575 
03576    /* Update the current periodic announcement to the next announcement */
03577    if (!qe->parent->randomperiodicannounce) {
03578       qe->last_periodic_announce_sound++;
03579    }
03580    
03581    return res;
03582 }

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

Definition at line 2750 of file app_queue.c.

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

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

02751 {
02752    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02753    int say_thanks = 1;
02754    time_t now;
02755 
02756    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02757    time(&now);
02758    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02759       return 0;
02760 
02761    /* If either our position has changed, or we are over the freq timer, say position */
02762    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02763       return 0;
02764 
02765    if (ringing) {
02766       ast_indicate(qe->chan,-1);
02767    } else {
02768       ast_moh_stop(qe->chan);
02769    }
02770 
02771    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02772       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02773       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02774       qe->pos <= qe->parent->announcepositionlimit))
02775          announceposition = 1;
02776 
02777 
02778    if (announceposition == 1) {
02779       /* Say we're next, if we are */
02780       if (qe->pos == 1) {
02781          res = play_file(qe->chan, qe->parent->sound_next);
02782          if (res)
02783             goto playout;
02784          else
02785             goto posout;
02786       } else {
02787          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02788             /* More than Case*/
02789             res = play_file(qe->chan, qe->parent->queue_quantity1);
02790             if (res)
02791                goto playout;
02792             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02793             if (res)
02794                goto playout;
02795          } else {
02796             /* Normal Case */
02797             res = play_file(qe->chan, qe->parent->sound_thereare);
02798             if (res)
02799                goto playout;
02800             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02801             if (res)
02802                goto playout;
02803          }
02804          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02805             /* More than Case*/
02806             res = play_file(qe->chan, qe->parent->queue_quantity2);
02807             if (res)
02808                goto playout;
02809          } else {
02810             res = play_file(qe->chan, qe->parent->sound_calls);
02811             if (res)
02812                goto playout;
02813          }
02814       }
02815    }
02816    /* Round hold time to nearest minute */
02817    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02818 
02819    /* If they have specified a rounding then round the seconds as well */
02820    if (qe->parent->roundingseconds) {
02821       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02822       avgholdsecs *= qe->parent->roundingseconds;
02823    } else {
02824       avgholdsecs = 0;
02825    }
02826 
02827    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02828 
02829    /* If the hold time is >1 min, if it's enabled, and if it's not
02830       supposed to be only once and we have already said it, say it */
02831     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02832         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02833         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02834       res = play_file(qe->chan, qe->parent->sound_holdtime);
02835       if (res)
02836          goto playout;
02837 
02838       if (avgholdmins >= 1) {
02839          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02840          if (res)
02841             goto playout;
02842 
02843          if (avgholdmins == 1) {
02844             res = play_file(qe->chan, qe->parent->sound_minute);
02845             if (res)
02846                goto playout;
02847          } else {
02848             res = play_file(qe->chan, qe->parent->sound_minutes);
02849             if (res)
02850                goto playout;
02851          }
02852       }
02853       if (avgholdsecs >= 1) {
02854          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02855          if (res)
02856             goto playout;
02857 
02858          res = play_file(qe->chan, qe->parent->sound_seconds);
02859          if (res)
02860             goto playout;
02861       }
02862    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02863       say_thanks = 0;
02864    }
02865 
02866 posout:
02867    if (qe->parent->announceposition) {
02868       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02869          qe->chan->name, qe->parent->name, qe->pos);
02870    }
02871    if (say_thanks) {
02872       res = play_file(qe->chan, qe->parent->sound_thanks);
02873    }
02874 playout:
02875 
02876    if ((res > 0 && !valid_exit(qe, res)))
02877       res = 0;
02878 
02879    /* Set our last_pos indicators */
02880    qe->last_pos = now;
02881    qe->last_pos_said = qe->pos;
02882 
02883    /* Don't restart music on hold if we're about to exit the caller from the queue */
02884    if (!res) {
02885       if (ringing) {
02886          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02887       } else {
02888          ast_moh_start(qe->chan, qe->moh, NULL);
02889       }
02890    }
02891    return res;
02892 }

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

04495 {
04496    const char *reason = NULL; /* silence dumb compilers */
04497 
04498    if (!qe->parent->eventwhencalled)
04499       return;
04500 
04501    switch (rsn) {
04502    case CALLER:
04503       reason = "caller";
04504       break;
04505    case AGENT:
04506       reason = "agent";
04507       break;
04508    case TRANSFER:
04509       reason = "transfer";
04510       break;
04511    }
04512 
04513    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04514       "Queue: %s\r\n"
04515       "Uniqueid: %s\r\n"
04516       "Channel: %s\r\n"
04517       "Member: %s\r\n"
04518       "MemberName: %s\r\n"
04519       "HoldTime: %ld\r\n"
04520       "TalkTime: %ld\r\n"
04521       "Reason: %s\r\n"
04522       "%s",
04523       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04524       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04525       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04526 }

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

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

05663 {
05664    int found = 0;
05665    struct call_queue *q;
05666    struct member *mem;
05667    struct ao2_iterator queue_iter;
05668    int failed;
05669 
05670    /* Special event for when all queues are paused - individual events still generated */
05671    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05672    if (ast_strlen_zero(queuename))
05673       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05674 
05675    queue_iter = ao2_iterator_init(queues, 0);
05676    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05677       ao2_lock(q);
05678       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05679          if ((mem = interface_exists(q, interface))) {
05680             if (mem->paused == paused) {
05681                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05682             }
05683 
05684             failed = 0;
05685             if (mem->realtime) {
05686                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05687             }
05688          
05689             if (failed) {
05690                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05691                ao2_ref(mem, -1);
05692                ao2_unlock(q);
05693                queue_t_unref(q, "Done with iterator");
05694                continue;
05695             }  
05696             found++;
05697             mem->paused = paused;
05698 
05699             if (queue_persistent_members)
05700                dump_queue_members(q);
05701 
05702             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05703             
05704             if (!ast_strlen_zero(reason)) {
05705                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05706                   "Queue: %s\r\n"
05707                   "Location: %s\r\n"
05708                   "MemberName: %s\r\n"
05709                   "Paused: %d\r\n"
05710                   "Reason: %s\r\n",
05711                      q->name, mem->interface, mem->membername, paused, reason);
05712             } else {
05713                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05714                   "Queue: %s\r\n"
05715                   "Location: %s\r\n"
05716                   "MemberName: %s\r\n"
05717                   "Paused: %d\r\n",
05718                      q->name, mem->interface, mem->membername, paused);
05719             }
05720             ao2_ref(mem, -1);
05721          }
05722       }
05723       
05724       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05725          ao2_unlock(q);
05726          queue_t_unref(q, "Done with iterator");
05727          break;
05728       }
05729       
05730       ao2_unlock(q);
05731       queue_t_unref(q, "Done with iterator");
05732    }
05733    ao2_iterator_destroy(&queue_iter);
05734 
05735    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05736 }

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

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

05740 {
05741    int foundinterface = 0, foundqueue = 0;
05742    struct call_queue *q;
05743    struct member *mem;
05744    struct ao2_iterator queue_iter;
05745 
05746    if (penalty < 0) {
05747       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05748       return RESULT_FAILURE;
05749    }
05750 
05751    queue_iter = ao2_iterator_init(queues, 0);
05752    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05753       ao2_lock(q);
05754       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05755          foundqueue++;
05756          if ((mem = interface_exists(q, interface))) {
05757             foundinterface++;
05758             mem->penalty = penalty;
05759             
05760             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05761             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05762                "Queue: %s\r\n"
05763                "Location: %s\r\n"
05764                "Penalty: %d\r\n",
05765                q->name, mem->interface, penalty);
05766             ao2_ref(mem, -1);
05767          }
05768       }
05769       ao2_unlock(q);
05770       queue_t_unref(q, "Done with iterator");
05771    }
05772    ao2_iterator_destroy(&queue_iter);
05773 
05774    if (foundinterface) {
05775       return RESULT_SUCCESS;
05776    } else if (!foundqueue) {
05777       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05778    } else {
05779       ast_log (LOG_ERROR, "Invalid interface\n");
05780    }  
05781 
05782    return RESULT_FAILURE;
05783 }

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

sets the QUEUESTATUS channel variable

Definition at line 1247 of file app_queue.c.

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

Referenced by queue_exec().

01248 {
01249    int i;
01250 
01251    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
01252       if (queue_results[i].id == res) {
01253          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
01254          return;
01255       }
01256    }
01257 }

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

Set variables of queue.

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

01397 {
01398    char interfacevar[256]="";
01399    float sl = 0;
01400 
01401    ao2_lock(q);
01402 
01403    if (q->setqueuevar) {
01404       sl = 0;
01405       if (q->callscompleted > 0) 
01406          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01407 
01408       snprintf(interfacevar, sizeof(interfacevar),
01409          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01410          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01411 
01412       ao2_unlock(q);
01413    
01414       pbx_builtin_setvar_multiple(chan, interfacevar); 
01415    } else {
01416       ao2_unlock(q);
01417    }
01418 }

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

04598 {
04599    struct ast_datastore *ds;
04600    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04601 
04602    if (!qtds) {
04603       ast_log(LOG_WARNING, "Memory allocation error!\n");
04604       return NULL;
04605    }
04606 
04607    ast_channel_lock(qe->chan);
04608    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04609       ast_channel_unlock(qe->chan);
04610       ast_free(qtds);
04611       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04612       return NULL;
04613    }
04614 
04615    qtds->qe = qe;
04616    /* This member is refcounted in try_calling, so no need to add it here, too */
04617    qtds->member = member;
04618    qtds->starttime = starttime;
04619    qtds->callcompletedinsl = callcompletedinsl;
04620    ds->data = qtds;
04621    ast_channel_datastore_add(qe->chan, ds);
04622    ast_channel_unlock(qe->chan);
04623    return ds;
04624 }

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

03506 {
03507    struct callattempt *best = find_best(outgoing);
03508 
03509    if (best) {
03510       /* Ring just the best channel */
03511       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03512       qe->linpos = best->metric % 1000;
03513    } else {
03514       /* Just increment rrpos */
03515       if (qe->linwrapped) {
03516          /* No more channels, start over */
03517          qe->linpos = 0;
03518       } else {
03519          /* Prioritize next entry */
03520          qe->linpos++;
03521       }
03522    }
03523    qe->linwrapped = 0;
03524 
03525    return 0;
03526 }

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

03482 {
03483    struct callattempt *best = find_best(outgoing);
03484 
03485    if (best) {
03486       /* Ring just the best channel */
03487       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03488       qe->parent->rrpos = best->metric % 1000;
03489    } else {
03490       /* Just increment rrpos */
03491       if (qe->parent->wrapped) {
03492          /* No more channels, start over */
03493          qe->parent->rrpos = 0;
03494       } else {
03495          /* Prioritize next entry */
03496          qe->parent->rrpos++;
03497       }
03498    }
03499    qe->parent->wrapped = 0;
03500 
03501    return 0;
03502 }

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

Definition at line 1271 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

01272 {
01273    int x;
01274 
01275    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01276       if (!strcasecmp(strategy, strategies[x].name))
01277          return strategies[x].strategy;
01278    }
01279 
01280    return -1;
01281 }

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

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

static int unload_module ( void   )  [static]

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

08782 {
08783    int res;
08784    struct ast_context *con;
08785    struct ao2_iterator q_iter;
08786    struct call_queue *q = NULL;
08787 
08788    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08789    res = ast_manager_unregister("QueueStatus");
08790    res |= ast_manager_unregister("Queues");
08791    res |= ast_manager_unregister("QueueRule");
08792    res |= ast_manager_unregister("QueueSummary");
08793    res |= ast_manager_unregister("QueueAdd");
08794    res |= ast_manager_unregister("QueueRemove");
08795    res |= ast_manager_unregister("QueuePause");
08796    res |= ast_manager_unregister("QueueLog");
08797    res |= ast_manager_unregister("QueuePenalty");
08798    res |= ast_manager_unregister("QueueReload");
08799    res |= ast_manager_unregister("QueueReset");
08800    res |= ast_unregister_application(app_aqm);
08801    res |= ast_unregister_application(app_rqm);
08802    res |= ast_unregister_application(app_pqm);
08803    res |= ast_unregister_application(app_upqm);
08804    res |= ast_unregister_application(app_ql);
08805    res |= ast_unregister_application(app);
08806    res |= ast_custom_function_unregister(&queueexists_function);
08807    res |= ast_custom_function_unregister(&queuevar_function);
08808    res |= ast_custom_function_unregister(&queuemembercount_function);
08809    res |= ast_custom_function_unregister(&queuemembercount_dep);
08810    res |= ast_custom_function_unregister(&queuememberlist_function);
08811    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08812    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08813 
08814    res |= ast_data_unregister(NULL);
08815 
08816    if (device_state_sub)
08817       ast_event_unsubscribe(device_state_sub);
08818 
08819    ast_extension_state_del(0, extension_state_cb);
08820 
08821    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08822       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08823       ast_context_destroy(con, "app_queue"); /* leave no trace */
08824    }
08825 
08826    q_iter = ao2_iterator_init(queues, 0);
08827    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08828       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08829       queue_t_unref(q, "Done with iterator");
08830    }
08831    ao2_iterator_destroy(&q_iter);
08832    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08833    ao2_ref(queues, -1);
08834    ast_unload_realtime("queue_members");
08835    return res;
08836 }

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

04229 {
04230    int max_penalty = INT_MAX;
04231 
04232    if (qe->max_penalty != INT_MAX) {
04233       char max_penalty_str[20];
04234 
04235       if (qe->pr->max_relative) {
04236          max_penalty = qe->max_penalty + qe->pr->max_value;
04237       } else {
04238          max_penalty = qe->pr->max_value;
04239       }
04240 
04241       /* a relative change to the penalty could put it below 0 */
04242       if (max_penalty < 0) {
04243          max_penalty = 0;
04244       }
04245 
04246       snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
04247       pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
04248       qe->max_penalty = max_penalty;
04249       ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n",
04250          qe->max_penalty, qe->chan->name, qe->pr->time);
04251    }
04252 
04253    if (qe->min_penalty != INT_MAX) {
04254       char min_penalty_str[20];
04255       int min_penalty;
04256 
04257       if (qe->pr->min_relative) {
04258          min_penalty = qe->min_penalty + qe->pr->min_value;
04259       } else {
04260          min_penalty = qe->pr->min_value;
04261       }
04262 
04263       if (min_penalty < 0) {
04264          min_penalty = 0;
04265       }
04266 
04267       if (max_penalty != INT_MAX && min_penalty > max_penalty) {
04268          min_penalty = max_penalty;
04269       }
04270 
04271       snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
04272       pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
04273       qe->min_penalty = min_penalty;
04274       ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n",
04275          qe->min_penalty, qe->chan->name, qe->pr->time);
04276    }
04277 
04278    qe->pr = AST_LIST_NEXT(qe->pr, list);
04279 }

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

04368 {
04369    int oldtalktime;
04370 
04371    struct member *mem;
04372    struct call_queue *qtmp;
04373    struct ao2_iterator queue_iter;  
04374    
04375    if (shared_lastcall) {
04376       queue_iter = ao2_iterator_init(queues, 0);
04377       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04378          ao2_lock(qtmp);
04379          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04380             time(&mem->lastcall);
04381             mem->calls++;
04382             mem->lastqueue = q;
04383             ao2_ref(mem, -1);
04384          }
04385          ao2_unlock(qtmp);
04386          queue_t_unref(qtmp, "Done with iterator");
04387       }
04388       ao2_iterator_destroy(&queue_iter);
04389    } else {
04390       ao2_lock(q);
04391       time(&member->lastcall);
04392       member->calls++;
04393       member->lastqueue = q;
04394       ao2_unlock(q);
04395    }  
04396    ao2_lock(q);
04397    q->callscompleted++;
04398    if (callcompletedinsl)
04399       q->callscompletedinsl++;
04400    /* Calculate talktime using the same exponential average as holdtime code*/
04401    oldtalktime = q->talktime;
04402    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04403    ao2_unlock(q);
04404    return 0;
04405 }

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

Definition at line 2529 of file app_queue.c.

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

Referenced by set_member_paused().

02530 {
02531    int ret = -1;
02532 
02533    if (ast_strlen_zero(mem->rt_uniqueid))
02534       return ret;
02535 
02536    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02537       ret = 0;
02538 
02539    return ret;
02540 }

static void update_realtime_members ( struct call_queue q  )  [static]

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

02544 {
02545    struct ast_config *member_config = NULL;
02546    struct member *m;
02547    char *interface = NULL;
02548    struct ao2_iterator mem_iter;
02549 
02550    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02551       /* This queue doesn't have realtime members. If the queue still has any realtime
02552        * members in memory, they need to be removed.
02553        */
02554       ao2_lock(q);
02555       mem_iter = ao2_iterator_init(q->members, 0);
02556       while ((m = ao2_iterator_next(&mem_iter))) {
02557          if (m->realtime) {
02558             member_remove_from_queue(q, m);
02559          }
02560          ao2_ref(m, -1);
02561       }
02562       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02563       ao2_unlock(q);
02564       return;
02565    }
02566 
02567    ao2_lock(q);
02568 
02569    /* Temporarily set realtime  members dead so we can detect deleted ones.*/
02570    mem_iter = ao2_iterator_init(q->members, 0);
02571    while ((m = ao2_iterator_next(&mem_iter))) {
02572       if (m->realtime)
02573          m->dead = 1;
02574       ao2_ref(m, -1);
02575    }
02576    ao2_iterator_destroy(&mem_iter);
02577 
02578    while ((interface = ast_category_browse(member_config, interface))) {
02579       rt_handle_member_record(q, interface,
02580          ast_variable_retrieve(member_config, interface, "uniqueid"),
02581          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02582          ast_variable_retrieve(member_config, interface, "penalty"),
02583          ast_variable_retrieve(member_config, interface, "paused"),
02584          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02585    }
02586 
02587    /* Delete all realtime members that have been deleted in DB. */
02588    mem_iter = ao2_iterator_init(q->members, 0);
02589    while ((m = ao2_iterator_next(&mem_iter))) {
02590       if (m->dead) {
02591          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02592          member_remove_from_queue(q, m);
02593       }
02594       ao2_ref(m, -1);
02595    }
02596    ao2_iterator_destroy(&mem_iter);
02597    ao2_unlock(q);
02598    ast_config_destroy(member_config);
02599 }

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

01537 {
01538    m->status = status;
01539 
01540    if (q->maskmemberstatus)
01541       return 0;
01542 
01543    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01544       "Queue: %s\r\n"
01545       "Location: %s\r\n"
01546       "MemberName: %s\r\n"
01547       "Membership: %s\r\n"
01548       "Penalty: %d\r\n"
01549       "CallsTaken: %d\r\n"
01550       "LastCall: %d\r\n"
01551       "Status: %d\r\n"
01552       "Paused: %d\r\n",
01553       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01554       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01555    );
01556 
01557    return 0;
01558 }

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

UnPauseQueueMember application.

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

05952 {
05953    char *parse;
05954    AST_DECLARE_APP_ARGS(args,
05955       AST_APP_ARG(queuename);
05956       AST_APP_ARG(interface);
05957       AST_APP_ARG(options);
05958       AST_APP_ARG(reason);
05959    );
05960 
05961    if (ast_strlen_zero(data)) {
05962       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05963       return -1;
05964    }
05965 
05966    parse = ast_strdupa(data);
05967 
05968    AST_STANDARD_APP_ARGS(args, parse);
05969 
05970    if (ast_strlen_zero(args.interface)) {
05971       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05972       return -1;
05973    }
05974 
05975    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05976       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05977       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05978       return 0;
05979    }
05980 
05981    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05982 
05983    return 0;
05984 }

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

02717 {
02718    int digitlen = strlen(qe->digits);
02719 
02720    /* Prevent possible buffer overflow */
02721    if (digitlen < sizeof(qe->digits) - 2) {
02722       qe->digits[digitlen] = digit;
02723       qe->digits[digitlen + 1] = '\0';
02724    } else {
02725       qe->digits[0] = '\0';
02726       return 0;
02727    }
02728 
02729    /* If there's no context to goto, short-circuit */
02730    if (ast_strlen_zero(qe->context))
02731       return 0;
02732 
02733    /* If the extension is bad, then reset the digits to blank */
02734    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02735       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02736       qe->digits[0] = '\0';
02737       return 0;
02738    }
02739 
02740    /* We have an exact match */
02741    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02742       qe->valid_digits = 1;
02743       /* Return 1 on a successful goto */
02744       return 1;
02745    }
02746 
02747    return 0;
02748 }

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

03105 {
03106    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
03107    const char *tmp;
03108 
03109    if (pbx_builtin_serialize_variables(chan, &buf)) {
03110       int i, j;
03111 
03112       /* convert "\n" to "\nVariable: " */
03113       strcpy(vars, "Variable: ");
03114       tmp = ast_str_buffer(buf);
03115 
03116       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
03117          vars[j] = tmp[i];
03118 
03119          if (tmp[i + 1] == '\0')
03120             break;
03121          if (tmp[i] == '\n') {
03122             vars[j++] = '\r';
03123             vars[j++] = '\n';
03124 
03125             ast_copy_string(&(vars[j]), "Variable: ", len - j);
03126             j += 9;
03127          }
03128       }
03129       if (j > len - 3)
03130          j = len - 3;
03131       vars[j++] = '\r';
03132       vars[j++] = '\n';
03133       vars[j] = '\0';
03134    } else {
03135       /* there are no channel variables; leave it blank */
03136       *vars = '\0';
03137    }
03138    return vars;
03139 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5473 of file app_queue.c.

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

Referenced by queue_exec().

05474 {
05475    /* Don't need to hold the lock while we setup the outgoing calls */
05476    int retrywait = qe->parent->retry * 1000;
05477 
05478    int res = ast_waitfordigit(qe->chan, retrywait);
05479    if (res > 0 && !valid_exit(qe, res))
05480       res = 0;
05481 
05482    return res;
05483 }

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

Wait for a member to answer the call.

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

Definition at line 3668 of file app_queue.c.

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

Referenced by try_calling().

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

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

04292 {
04293    int res = 0;
04294 
04295    /* This is the holding pen for callers 2 through maxlen */
04296    for (;;) {
04297 
04298       if (is_our_turn(qe))
04299          break;
04300 
04301       /* If we have timed out, break out */
04302       if (qe->expire && (time(NULL) >= qe->expire)) {
04303          *reason = QUEUE_TIMEOUT;
04304          break;
04305       }
04306 
04307       if (qe->parent->leavewhenempty) {
04308          int status = 0;
04309 
04310          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty, 0))) {
04311             *reason = QUEUE_LEAVEEMPTY;
04312             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04313             leave_queue(qe);
04314             break;
04315          }
04316       }
04317 
04318       /* Make a position announcement, if enabled */
04319       if (qe->parent->announcefrequency &&
04320          (res = say_position(qe,ringing)))
04321          break;
04322 
04323       /* If we have timed out, break out */
04324       if (qe->expire && (time(NULL) >= qe->expire)) {
04325          *reason = QUEUE_TIMEOUT;
04326          break;
04327       }
04328 
04329       /* Make a periodic announcement, if enabled */
04330       if (qe->parent->periodicannouncefrequency &&
04331          (res = say_periodic_announcement(qe,ringing)))
04332          break;
04333       
04334       /* see if we need to move to the next penalty level for this queue */
04335       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04336          update_qe_rule(qe);
04337       }
04338 
04339       /* If we have timed out, break out */
04340       if (qe->expire && (time(NULL) >= qe->expire)) {
04341          *reason = QUEUE_TIMEOUT;
04342          break;
04343       }
04344       
04345       /* Wait a second before checking again */
04346       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04347          if (res > 0 && !valid_exit(qe, res))
04348             res = 0;
04349          else
04350             break;
04351       }
04352       
04353       /* If we have timed out, break out */
04354       if (qe->expire && (time(NULL) >= qe->expire)) {
04355          *reason = QUEUE_TIMEOUT;
04356          break;
04357       }
04358    }
04359 
04360    return res;
04361 }

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

Referenced by complete_queue().

07455                                                             {
07456    int list_len, word_len = strlen(word);
07457    const char *find, *end_find, *end_list;
07458 
07459    /* strip whitespace from front */
07460    while (isspace(*list)) {
07461       list++;
07462    }
07463 
07464    while ((find = strstr(list, word))) {
07465       /* beginning of find starts inside another word? */
07466       if (find != list && *(find - 1) != ' ') {
07467          list = find;
07468          /* strip word from front */
07469          while (!isspace(*list) && *list != '\0') {
07470             list++;
07471          }
07472          /* strip whitespace from front */
07473          while (isspace(*list)) {
07474             list++;
07475          }
07476          continue;
07477       }
07478 
07479       /* end of find ends inside another word or at very end of list? */
07480       list_len = strlen(list);
07481       end_find = find + word_len;
07482       end_list = list + list_len;
07483       if (end_find == end_list || *end_find != ' ') {
07484          list = find;
07485          /* strip word from front */
07486          while (!isspace(*list) && *list != '\0') {
07487             list++;
07488          }
07489          /* strip whitespace from front */
07490          while (isspace(*list)) {
07491             list++;
07492          }
07493          continue;
07494       }
07495 
07496       /* terminating conditions satisfied, word at beginning or separated by ' ' */
07497       return 1;
07498    }
07499    
07500    return 0;
07501 }


Variable Documentation

char* app = "Queue" [static]

Definition at line 951 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 953 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 957 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 961 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 955 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 959 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 973 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

Definition at line 8499 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 982 of file app_queue.c.

Definition at line 935 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 976 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 964 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 8490 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 8496 of file app_queue.c.

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

Definition at line 8777 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

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

Definition at line 6812 of file app_queue.c.

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

Definition at line 6827 of file app_queue.c.

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

Definition at line 6822 of file app_queue.c.

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

Definition at line 6837 of file app_queue.c.

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

Definition at line 6842 of file app_queue.c.

struct ao2_container* queues [static]
Initial value:

Definition at line 8772 of file app_queue.c.

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

Definition at line 6817 of file app_queue.c.

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

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

int shared_lastcall = 1 [static]

queues.conf [general] option

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

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 970 of file app_queue.c.


Generated on 15 Apr 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1