Sat Feb 11 06:33:53 2012

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  rule_lists
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 PM_MAX_LEN   8192
#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 void __reg_module (void)
static void __unreg_module (void)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
 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 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 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)
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 (const char *context, const 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_load_queue_rt_friendly (const char *queuename)
static struct memberfind_member_by_queuename_and_interface (const char *queuename, const char *interface)
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
 Iterate through queue's member list and delete them.
static int get_member_penalty (char *queuename, char *interface)
static int get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
 Check if members are available.
static int get_queue_member_status (struct member *cur)
 Return the current state of a member.
static char * handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_reset (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int handle_statechange (void *datap)
 set a member's status based on device state of that member's interface
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
 Hang up a list of outgoing calls.
static void init_queue (struct call_queue *q)
 Initialize Queue default values.
static void insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 Insert the 'new' entry after the 'prev' entry of queue 'q'.
static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
 Change queue penalty by adding rule.
static const char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int is_our_turn (struct queue_ent *qe)
 Check if we should start attempting to call queue members.
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
static int kill_dead_members (void *obj, void *arg, int flags)
static int kill_dead_queues (void *obj, void *arg, int flags)
static void leave_queue (struct queue_ent *qe)
 Caller leaving queue.
static int load_module (void)
static 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 int member_cmp_fn (void *obj1, void *obj2, int flags)
static int member_hash_fn (const void *obj, const int flags)
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_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_mem_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get number either busy / free / ready or total members of a specific queue.
static int queue_function_mem_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy.
static int queue_function_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.
static int queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.
static int queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get the total number of members in a specific queue (Deprecated).
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 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 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, struct ast_config *member_config)
 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 int set_member_penalty_help_members (struct call_queue *q, 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 update_connectedline)
 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.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", }
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 struct ast_module_infoast_module_info = &__mod_info
static int autofill_default = 1
 queues.conf [general] option
static struct autopause autopausesmodes []
static int check_state_unknown = 0
 queues.conf [general] option
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 log_membername_as_agent = 0
 queues.conf [general] option
static int montype_default = 0
 queues.conf [general] option
static int negative_penalty_invalid = 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 1087 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 1088 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 1103 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 1102 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 1101 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 1100 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 3534 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 8356 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8420 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 902 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 908 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 931 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 1089 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 1301 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 1303 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 1304 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 904 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 851 of file app_queue.c.

anonymous enum

Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 862 of file app_queue.c.

00862      {
00863      QUEUE_AUTOPAUSE_OFF = 0,
00864      QUEUE_AUTOPAUSE_ON,
00865      QUEUE_AUTOPAUSE_ALL
00866 };

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4233 of file app_queue.c.

04233                            {
04234    CALLER,
04235    AGENT,
04236    TRANSFER
04237 };

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

01075                       {
01076    QUEUE_EMPTY_PENALTY = (1 << 0),
01077    QUEUE_EMPTY_PAUSED = (1 << 1),
01078    QUEUE_EMPTY_INUSE = (1 << 2),
01079    QUEUE_EMPTY_RINGING = (1 << 3),
01080    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
01081    QUEUE_EMPTY_INVALID = (1 << 5),
01082    QUEUE_EMPTY_UNKNOWN = (1 << 6),
01083    QUEUE_EMPTY_WRAPUP = (1 << 7),
01084 };

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 868 of file app_queue.c.

00868                        {
00869    QUEUE_RELOAD_PARAMETERS = (1 << 0),
00870    QUEUE_RELOAD_MEMBER = (1 << 1),
00871    QUEUE_RELOAD_RULES = (1 << 2),
00872    QUEUE_RESET_STATS = (1 << 3),
00873 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 963 of file app_queue.c.

00963                   {
00964    QUEUE_UNKNOWN = 0,
00965    QUEUE_TIMEOUT = 1,
00966    QUEUE_JOINEMPTY = 2,
00967    QUEUE_LEAVEEMPTY = 3,
00968    QUEUE_JOINUNAVAIL = 4,
00969    QUEUE_LEAVEUNAVAIL = 5,
00970    QUEUE_FULL = 6,
00971    QUEUE_CONTINUE = 7,
00972 };

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 988 of file app_queue.c.

00988                             {
00989    TIMEOUT_PRIORITY_APP,
00990    TIMEOUT_PRIORITY_CONF,
00991 };


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 7210 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_channel_name(), 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, find_load_queue_rt_friendly(), call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, 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::state_interface, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

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

static void __reg_module ( void   )  [static]

Definition at line 8777 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 8777 of file app_queue.c.

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

References ao2_link, ao2_lock, ao2_ref, ao2_unlock, member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, find_load_queue_rt_friendly(), member::interface, interface_exists(), member::lastcall, manager_event, member::membername, call_queue::members, call_queue::name, 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().

05351 {
05352    struct call_queue *q;
05353    struct member *new_member, *old_member;
05354    int res = RES_NOSUCHQUEUE;
05355 
05356    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05357     * short-circuits if the queue is already in memory. */
05358    if (!(q = find_load_queue_rt_friendly(queuename))) {
05359       return res;
05360    }
05361 
05362    ao2_lock(q);
05363    if ((old_member = interface_exists(q, interface)) == NULL) {
05364       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05365          new_member->dynamic = 1;
05366          ao2_link(q->members, new_member);
05367          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05368             "Queue: %s\r\n"
05369             "Location: %s\r\n"
05370             "MemberName: %s\r\n"
05371             "StateInterface: %s\r\n"
05372             "Membership: %s\r\n"
05373             "Penalty: %d\r\n"
05374             "CallsTaken: %d\r\n"
05375             "LastCall: %d\r\n"
05376             "Status: %d\r\n"
05377             "Paused: %d\r\n",
05378             q->name, new_member->interface, new_member->membername, state_interface,
05379             "dynamic",
05380             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05381             new_member->status, new_member->paused);
05382 
05383          ao2_ref(new_member, -1);
05384          new_member = NULL;
05385 
05386          if (dump) {
05387             dump_queue_members(q);
05388          }
05389 
05390          res = RES_OKAY;
05391       } else {
05392          res = RES_OUTOFMEMORY;
05393       }
05394    } else {
05395       ao2_ref(old_member, -1);
05396       res = RES_EXISTS;
05397    }
05398    ao2_unlock(q);
05399    queue_t_unref(q, "Expiring temporary reference");
05400 
05401    return res;
05402 }

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

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

02243 {
02244    struct call_queue *q;
02245 
02246    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02247       if (ast_string_field_init(q, 64)) {
02248          queue_t_unref(q, "String field allocation failed");
02249          return NULL;
02250       }
02251       ast_string_field_set(q, name, queuename);
02252    }
02253    return q;
02254 }

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

AddQueueMember application.

Definition at line 5849 of file app_queue.c.

References add_to_queue(), args, AST_APP_ARG, ast_channel_name(), ast_channel_uniqueid(), AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), member::interface, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, parse(), pbx_builtin_setvar_helper(), member::penalty, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and member::state_interface.

Referenced by load_module().

05850 {
05851    int res=-1;
05852    char *parse, *temppos = NULL;
05853    AST_DECLARE_APP_ARGS(args,
05854       AST_APP_ARG(queuename);
05855       AST_APP_ARG(interface);
05856       AST_APP_ARG(penalty);
05857       AST_APP_ARG(options);
05858       AST_APP_ARG(membername);
05859       AST_APP_ARG(state_interface);
05860    );
05861    int penalty = 0;
05862 
05863    if (ast_strlen_zero(data)) {
05864       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05865       return -1;
05866    }
05867 
05868    parse = ast_strdupa(data);
05869 
05870    AST_STANDARD_APP_ARGS(args, parse);
05871 
05872    if (ast_strlen_zero(args.interface)) {
05873       args.interface = ast_strdupa(ast_channel_name(chan));
05874       temppos = strrchr(args.interface, '-');
05875       if (temppos)
05876          *temppos = '\0';
05877    }
05878 
05879    if (!ast_strlen_zero(args.penalty)) {
05880       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05881          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05882          penalty = 0;
05883       }
05884    }
05885 
05886    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05887    case RES_OKAY:
05888       if (ast_strlen_zero(args.membername) || !log_membername_as_agent) {
05889          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "ADDMEMBER", "%s", "");
05890       } else {
05891          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.membername, "ADDMEMBER", "%s", "");
05892       }
05893       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05894       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05895       res = 0;
05896       break;
05897    case RES_EXISTS:
05898       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
05899       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
05900       res = 0;
05901       break;
05902    case RES_NOSUCHQUEUE:
05903       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
05904       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
05905       res = 0;
05906       break;
05907    case RES_OUTOFMEMORY:
05908       ast_log(LOG_ERROR, "Out of memory adding interface %s to queue %s\n", args.interface, args.queuename);
05909       break;
05910    }
05911 
05912    return res;
05913 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

04338 {
04339    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04340 }

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

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

01261 {
01262    int x;
01263    /*This 'double check' that default value is OFF */
01264    if (ast_strlen_zero(autopause))
01265       return QUEUE_AUTOPAUSE_OFF;
01266 
01267    /*This 'double check' is to ensure old values works */
01268    if(ast_true(autopause))
01269       return QUEUE_AUTOPAUSE_ON;
01270 
01271    for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
01272       if (!strcasecmp(autopause, autopausesmodes[x].name))
01273          return autopausesmodes[x].autopause;
01274    }
01275 
01276    /*This 'double check' that default value is OFF */
01277    return QUEUE_AUTOPAUSE_OFF;
01278 }

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 4164 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, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

04165 {
04166    /* disregarding penalty on too few members? */
04167    int membercount = ao2_container_count(q->members);
04168    unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1;
04169 
04170    if (usepenalty) {
04171       if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
04172          (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
04173          return -1;
04174       }
04175    } else {
04176       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04177            membercount, q->penaltymemberslimit);
04178    }
04179 
04180    switch (q->strategy) {
04181    case QUEUE_STRATEGY_RINGALL:
04182       /* Everyone equal, except for penalty */
04183       tmp->metric = mem->penalty * 1000000 * usepenalty;
04184       break;
04185    case QUEUE_STRATEGY_LINEAR:
04186       if (pos < qe->linpos) {
04187          tmp->metric = 1000 + pos;
04188       } else {
04189          if (pos > qe->linpos)
04190             /* Indicate there is another priority */
04191             qe->linwrapped = 1;
04192          tmp->metric = pos;
04193       }
04194       tmp->metric += mem->penalty * 1000000 * usepenalty;
04195       break;
04196    case QUEUE_STRATEGY_RRORDERED:
04197    case QUEUE_STRATEGY_RRMEMORY:
04198       if (pos < q->rrpos) {
04199          tmp->metric = 1000 + pos;
04200       } else {
04201          if (pos > q->rrpos)
04202             /* Indicate there is another priority */
04203             q->wrapped = 1;
04204          tmp->metric = pos;
04205       }
04206       tmp->metric += mem->penalty * 1000000 * usepenalty;
04207       break;
04208    case QUEUE_STRATEGY_RANDOM:
04209       tmp->metric = ast_random() % 1000;
04210       tmp->metric += mem->penalty * 1000000 * usepenalty;
04211       break;
04212    case QUEUE_STRATEGY_WRANDOM:
04213       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04214       break;
04215    case QUEUE_STRATEGY_FEWESTCALLS:
04216       tmp->metric = mem->calls;
04217       tmp->metric += mem->penalty * 1000000 * usepenalty;
04218       break;
04219    case QUEUE_STRATEGY_LEASTRECENT:
04220       if (!mem->lastcall)
04221          tmp->metric = 0;
04222       else
04223          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04224       tmp->metric += mem->penalty * 1000000 * usepenalty;
04225       break;
04226    default:
04227       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04228       break;
04229    }
04230    return 0;
04231 }

static void callattempt_free ( struct callattempt doomed  )  [static]

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

02906 {
02907    if (doomed->member) {
02908       ao2_ref(doomed->member, -1);
02909    }
02910    ast_party_connected_line_free(&doomed->connected);
02911    ast_free(doomed);
02912 }

static void clear_queue ( struct call_queue q  )  [static]

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

01779 {
01780    q->holdtime = 0;
01781    q->callscompleted = 0;
01782    q->callsabandoned = 0;
01783    q->callscompletedinsl = 0;
01784    q->talktime = 0;
01785 
01786    if (q->members) {
01787       struct member *mem;
01788       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01789       while ((mem = ao2_iterator_next(&mem_iter))) {
01790          mem->calls = 0;
01791          mem->lastcall = 0;
01792          ao2_ref(mem, -1);
01793       }
01794       ao2_iterator_destroy(&mem_iter);
01795    }
01796 }

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 7149 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(), call_queue::name, queue_t_unref, and queues.

Referenced by reload_handler().

07150 {
07151    struct call_queue *q;
07152    struct ao2_iterator queue_iter;
07153 
07154    queue_iter = ao2_iterator_init(queues, 0);
07155    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07156       ao2_lock(q);
07157       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
07158          clear_queue(q);
07159       ao2_unlock(q);
07160       queue_t_unref(q, "Done with iterator");
07161    }
07162    ao2_iterator_destroy(&queue_iter);
07163    return 0;
07164 }

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

Definition at line 2993 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, call_queue::name, num_available_members(), OBJ_POINTER, queue_t_unref, queues, and call_queue::weight.

Referenced by ring_entry().

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

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

Definition at line 7349 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_strdup, call_queue::name, queue_t_unref, and queues.

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

07350 {
07351    struct call_queue *q;
07352    char *ret = NULL;
07353    int which = 0;
07354    int wordlen = strlen(word);
07355    struct ao2_iterator queue_iter;
07356 
07357    queue_iter = ao2_iterator_init(queues, 0);
07358    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07359       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
07360          ret = ast_strdup(q->name);
07361          queue_t_unref(q, "Done with iterator");
07362          break;
07363       }
07364       queue_t_unref(q, "Done with iterator");
07365    }
07366    ao2_iterator_destroy(&queue_iter);
07367 
07368    return ret;
07369 }

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

Definition at line 7813 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07814 {
07815    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07816    switch (pos) {
07817    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07818       return NULL;
07819    case 4: /* only one possible match, "to" */
07820       return state == 0 ? ast_strdup("to") : NULL;
07821    case 5: /* <queue> */
07822       return complete_queue(line, word, pos, state);
07823    case 6: /* only one possible match, "penalty" */
07824       return state == 0 ? ast_strdup("penalty") : NULL;
07825    case 7:
07826       if (state < 100) {      /* 0-99 */
07827          char *num;
07828          if ((num = ast_malloc(3))) {
07829             sprintf(num, "%d", state);
07830          }
07831          return num;
07832       } else {
07833          return NULL;
07834       }
07835    case 8: /* only one possible match, "as" */
07836       return state == 0 ? ast_strdup("as") : NULL;
07837    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
07838       return NULL;
07839    default:
07840       return NULL;
07841    }
07842 }

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

Definition at line 8049 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

08050 {
08051    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
08052    switch (pos) {
08053    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
08054       return NULL;
08055    case 4:  /* only one possible match, "queue" */
08056       return state == 0 ? ast_strdup("queue") : NULL;
08057    case 5:  /* <queue> */
08058       return complete_queue(line, word, pos, state);
08059    case 6: /* "reason" */
08060       return state == 0 ? ast_strdup("reason") : NULL;
08061    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
08062       return NULL;
08063    default:
08064       return NULL;
08065    }
08066 }

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

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

07948 {
07949    int which = 0;
07950    struct call_queue *q;
07951    struct member *m;
07952    struct ao2_iterator queue_iter;
07953    struct ao2_iterator mem_iter;
07954    int wordlen = strlen(word);
07955 
07956    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
07957    if (pos > 5 || pos < 3)
07958       return NULL;
07959    if (pos == 4)   /* only one possible match, 'from' */
07960       return (state == 0 ? ast_strdup("from") : NULL);
07961 
07962    if (pos == 5)   /* No need to duplicate code */
07963       return complete_queue(line, word, pos, state);
07964 
07965    /* here is the case for 3, <member> */
07966    queue_iter = ao2_iterator_init(queues, 0);
07967    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07968       ao2_lock(q);
07969       mem_iter = ao2_iterator_init(q->members, 0);
07970       while ((m = ao2_iterator_next(&mem_iter))) {
07971          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
07972             char *tmp;
07973             tmp = ast_strdup(m->interface);
07974             ao2_ref(m, -1);
07975             ao2_iterator_destroy(&mem_iter);
07976             ao2_unlock(q);
07977             queue_t_unref(q, "Done with iterator, returning interface name");
07978             ao2_iterator_destroy(&queue_iter);
07979             return tmp;
07980          }
07981          ao2_ref(m, -1);
07982       }
07983       ao2_iterator_destroy(&mem_iter);
07984       ao2_unlock(q);
07985       queue_t_unref(q, "Done with iterator");
07986    }
07987    ao2_iterator_destroy(&queue_iter);
07988 
07989    return NULL;
07990 }

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

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

08183 {
08184    int which = 0;
08185    struct rule_list *rl_iter;
08186    int wordlen = strlen(word);
08187    char *ret = NULL;
08188    if (pos != 3) /* Wha? */ {
08189       return NULL;
08190    }
08191 
08192    AST_LIST_LOCK(&rule_lists);
08193    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08194       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
08195          ret = ast_strdup(rl_iter->name);
08196          break;
08197       }
08198    }
08199    AST_LIST_UNLOCK(&rule_lists);
08200 
08201    return ret;
08202 }

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

Definition at line 8119 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

08120 {
08121    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
08122    switch (pos) {
08123    case 4:
08124       if (state == 0) {
08125          return ast_strdup("on");
08126       } else {
08127          return NULL;
08128       }
08129    case 6:
08130       if (state == 0) {
08131          return ast_strdup("in");
08132       } else {
08133          return NULL;
08134       }
08135    case 7:
08136       return complete_queue(line, word, pos, state);
08137    default:
08138       return NULL;
08139    }
08140 }

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

Definition at line 7371 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07372 {
07373    if (pos == 2)
07374       return complete_queue(line, word, pos, state);
07375    return NULL;
07376 }

static int compress_char ( const char  c  )  [static]

Definition at line 1662 of file app_queue.c.

Referenced by member_hash_fn().

01663 {
01664    if (c < 32)
01665       return 0;
01666    else if (c > 96)
01667       return c - 64;
01668    else
01669       return c - 32;
01670 }

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

Copy rule from global list into specified queue.

Definition at line 5950 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(), call_queue::defaultrule, LOG_ERROR, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, queue_ent::parent, queue_ent::qe_rules, rule_list::rules, and penalty_rule::time.

Referenced by queue_exec().

05951 {
05952    struct penalty_rule *pr_iter;
05953    struct rule_list *rl_iter;
05954    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
05955    AST_LIST_LOCK(&rule_lists);
05956    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05957       if (!strcasecmp(rl_iter->name, tmp))
05958          break;
05959    }
05960    if (rl_iter) {
05961       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05962          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05963          if (!new_pr) {
05964             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05965             break;
05966          }
05967          new_pr->time = pr_iter->time;
05968          new_pr->max_value = pr_iter->max_value;
05969          new_pr->min_value = pr_iter->min_value;
05970          new_pr->max_relative = pr_iter->max_relative;
05971          new_pr->min_relative = pr_iter->min_relative;
05972          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05973       }
05974    }
05975    AST_LIST_UNLOCK(&rule_lists);
05976 }

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 1627 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, member::status, and strsep().

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

01628 {
01629    struct member *cur;
01630 
01631    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01632       cur->penalty = penalty;
01633       cur->paused = paused;
01634       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01635       if (!ast_strlen_zero(state_interface)) {
01636          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01637       } else {
01638          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01639       }
01640       if (!ast_strlen_zero(membername)) {
01641          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01642       } else {
01643          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01644       }
01645       if (!strchr(cur->interface, '/')) {
01646          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01647       }
01648       if (!strncmp(cur->state_interface, "hint:", 5)) {
01649          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01650          char *exten = strsep(&context, "@") + 5;
01651 
01652          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01653          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01654       }
01655       cur->status = get_queue_member_status(cur);
01656    }
01657 
01658    return cur;
01659 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

02229 {
02230    struct call_queue *q = obj;
02231    int i;
02232 
02233    free_members(q, 1);
02234    ast_string_field_free_memory(q);
02235    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02236       if (q->sound_periodicannounce[i])
02237          free(q->sound_periodicannounce[i]);
02238    }
02239    ao2_ref(q->members, -1);
02240 }

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

Definition at line 1525 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(), LOG_ERROR, and statechange::state.

Referenced by load_module(), and load_pbx().

01526 {
01527    enum ast_device_state state;
01528    const char *device;
01529    struct statechange *sc;
01530    size_t datapsize;
01531 
01532    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01533    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01534 
01535    if (ast_strlen_zero(device)) {
01536       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01537       return;
01538    }
01539    datapsize = sizeof(*sc) + strlen(device) + 1;
01540    if (!(sc = ast_calloc(1, datapsize))) {
01541       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01542       return;
01543    }
01544    sc->state = state;
01545    strcpy(sc->dev, device);
01546    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01547       ast_free(sc);
01548    }
01549 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 3028 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

03029 {
03030    o->stillgoing = 0;
03031    ast_hangup(o->chan);
03032    o->chan = NULL;
03033 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

07197 {
07198    if (s)
07199       astman_append(s, "%s\r\n", str);
07200    else
07201       ast_cli(fd, "%s\n", str);
07202 }

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, PM_MAX_LEN, member::state_interface, and value.

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

05253 {
05254    struct member *cur_member;
05255    char value[PM_MAX_LEN];
05256    int value_len = 0;
05257    int res;
05258    struct ao2_iterator mem_iter;
05259 
05260    memset(value, 0, sizeof(value));
05261 
05262    if (!pm_queue)
05263       return;
05264 
05265    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05266    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05267       if (!cur_member->dynamic) {
05268          ao2_ref(cur_member, -1);
05269          continue;
05270       }
05271 
05272       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
05273          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
05274 
05275       ao2_ref(cur_member, -1);
05276 
05277       if (res != strlen(value + value_len)) {
05278          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
05279          break;
05280       }
05281       value_len += res;
05282    }
05283    ao2_iterator_destroy(&mem_iter);
05284    
05285    if (value_len && !cur_member) {
05286       if (ast_db_put(pm_family, pm_queue->name, value))
05287          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05288    } else
05289       /* Delete the entry if the queue is empty or there is an error */
05290       ast_db_del(pm_family, pm_queue->name);
05291 }

static void end_bridge_callback ( void *  data  )  [static]

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

04385 {
04386    struct queue_end_bridge *qeb = data;
04387    struct call_queue *q = qeb->q;
04388    struct ast_channel *chan = qeb->chan;
04389 
04390    if (ao2_ref(qeb, -1) == 1) {
04391       set_queue_variables(q, chan);
04392       /* This unrefs the reference we made in try_calling when we allocated qeb */
04393       queue_t_unref(q, "Expire bridge_config reference");
04394    }
04395 }

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

Definition at line 4377 of file app_queue.c.

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

04378 {
04379    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04380    ao2_ref(qeb, +1);
04381    qeb->chan = originator;
04382 }

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

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

01584 {
01585    struct ao2_iterator miter, qiter;
01586    struct member *m;
01587    struct call_queue *q;
01588    int found = 0, device_state = extensionstate2devicestate(state);
01589 
01590    qiter = ao2_iterator_init(queues, 0);
01591    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01592       ao2_lock(q);
01593 
01594       miter = ao2_iterator_init(q->members, 0);
01595       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01596          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01597             update_status(q, m, device_state);
01598             ao2_ref(m, -1);
01599             found = 1;
01600             break;
01601          }
01602       }
01603       ao2_iterator_destroy(&miter);
01604 
01605       ao2_unlock(q);
01606       queue_t_unref(q, "Done with iterator");
01607    }
01608    ao2_iterator_destroy(&qiter);
01609 
01610         if (found) {
01611       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01612    } else {
01613       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",
01614            exten, context, device_state, ast_devstate2str(device_state));
01615    }
01616 
01617    return 0;
01618 }

static int extensionstate2devicestate ( int  state  )  [static]

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

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

01553 {
01554    switch (state) {
01555    case AST_EXTENSION_NOT_INUSE:
01556       state = AST_DEVICE_NOT_INUSE;
01557       break;
01558    case AST_EXTENSION_INUSE:
01559       state = AST_DEVICE_INUSE;
01560       break;
01561    case AST_EXTENSION_BUSY:
01562       state = AST_DEVICE_BUSY;
01563       break;
01564    case AST_EXTENSION_RINGING:
01565       state = AST_DEVICE_RINGING;
01566       break;
01567    case AST_EXTENSION_ONHOLD:
01568       state = AST_DEVICE_ONHOLD;
01569       break;
01570    case AST_EXTENSION_UNAVAILABLE:
01571       state = AST_DEVICE_UNAVAILABLE;
01572       break;
01573    case AST_EXTENSION_REMOVED:
01574    case AST_EXTENSION_DEACTIVATED:
01575    default:
01576       state = AST_DEVICE_INVALID;
01577       break;
01578    }
01579 
01580    return state;
01581 }

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

find the entry with the best metric, or NULL

Definition at line 3285 of file app_queue.c.

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

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

03286 {
03287    struct callattempt *best = NULL, *cur;
03288 
03289    for (cur = outgoing; cur; cur = cur->q_next) {
03290       if (cur->stillgoing &&              /* Not already done */
03291          !cur->chan &&              /* Isn't already going */
03292          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03293          best = cur;
03294       }
03295    }
03296 
03297    return best;
03298 }

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

note

Note:
Load from realtime before taking the "queues" container lock, to avoid blocking all queue operations while waiting for the DB.
This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 2409 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(), call_queue::name, OBJ_POINTER, queue_t_unref, queues, call_queue::realtime, SENTINEL, update_realtime_members(), and call_queue::weight.

Referenced by __queues_show(), add_to_queue(), find_member_by_queuename_and_interface(), join_queue(), queue_function_exists(), queue_function_mem_read(), queue_function_mem_write(), queue_function_qac_dep(), queues_data_provider_get(), reload_queue_members(), and set_member_penalty().

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

static struct member * find_member_by_queuename_and_interface ( const char *  queuename,
const char *  interface 
) [static, read]

Definition at line 8757 of file app_queue.c.

References ao2_find, ao2_lock, ao2_unlock, find_load_queue_rt_friendly(), call_queue::members, OBJ_KEY, and queue_t_unref.

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

08758 {
08759    struct member *mem = NULL;
08760    struct call_queue *q;
08761 
08762    if ((q = find_load_queue_rt_friendly(queuename))) {
08763       ao2_lock(q);
08764       mem = ao2_find(q->members, interface, OBJ_KEY);
08765       ao2_unlock(q);
08766       queue_t_unref(q, "Expiring temporary reference.");
08767    }
08768    return mem;
08769 }

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 2266 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_unlink, ao2_unlock, ast_category_browse(), ast_copy_string(), ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), clear_queue(), member::dead, call_queue::dead, init_queue(), member::interface, LOG_WARNING, member::membername, call_queue::members, ast_variable::name, call_queue::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(), strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by find_load_queue_rt_friendly().

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

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

Iterate through queue's member list and delete them.

Definition at line 2212 of file app_queue.c.

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

Referenced by destroy_queue().

02213 {
02214    /* Free non-dynamic members */
02215    struct member *cur;
02216    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02217 
02218    while ((cur = ao2_iterator_next(&mem_iter))) {
02219       if (all || !cur->dynamic) {
02220          ao2_unlink(q->members, cur);
02221       }
02222       ao2_ref(cur, -1);
02223    }
02224    ao2_iterator_destroy(&mem_iter);
02225 }

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

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

05579 {
05580    int foundqueue = 0, penalty;
05581    struct call_queue *q, tmpq = {
05582       .name = queuename,
05583    };
05584    struct member *mem;
05585 
05586    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05587       foundqueue = 1;
05588       ao2_lock(q);
05589       if ((mem = interface_exists(q, interface))) {
05590          penalty = mem->penalty;
05591          ao2_ref(mem, -1);
05592          ao2_unlock(q);
05593          queue_t_unref(q, "Search complete");
05594          return penalty;
05595       }
05596       ao2_unlock(q);
05597       queue_t_unref(q, "Search complete");
05598    }
05599 
05600    /* some useful debuging */
05601    if (foundqueue) {
05602       ast_log (LOG_ERROR, "Invalid queuename\n");
05603    } else {
05604       ast_log (LOG_ERROR, "Invalid interface\n");
05605    }
05606 
05607    return RESULT_FAILURE;
05608 }

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

Check if members are available.

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

Definition at line 1374 of file app_queue.c.

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

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

01375 {
01376    struct member *member;
01377    struct ao2_iterator mem_iter;
01378 
01379    ao2_lock(q);
01380    mem_iter = ao2_iterator_init(q->members, 0);
01381    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01382       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
01383          if (conditions & QUEUE_EMPTY_PENALTY) {
01384             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01385             continue;
01386          }
01387       }
01388 
01389       switch (member->status) {
01390       case AST_DEVICE_INVALID:
01391          if (conditions & QUEUE_EMPTY_INVALID) {
01392             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01393             break;
01394          }
01395          goto default_case;
01396       case AST_DEVICE_UNAVAILABLE:
01397          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01398             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01399             break;
01400          }
01401          goto default_case;
01402       case AST_DEVICE_INUSE:
01403          if (conditions & QUEUE_EMPTY_INUSE) {
01404             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01405             break;
01406          }
01407          goto default_case;
01408       case AST_DEVICE_RINGING:
01409          if (conditions & QUEUE_EMPTY_RINGING) {
01410             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01411             break;
01412          }
01413          goto default_case;
01414       case AST_DEVICE_UNKNOWN:
01415          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01416             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01417             break;
01418          }
01419          /* Fall-through */
01420       default:
01421       default_case:
01422          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01423             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01424             break;
01425          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01426             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);
01427             break;
01428          } else {
01429             ao2_ref(member, -1);
01430             ao2_iterator_destroy(&mem_iter);
01431             ao2_unlock(q);
01432             ast_debug(4, "%s is available.\n", member->membername);
01433             return 0;
01434          }
01435          break;
01436       }
01437    }
01438    ao2_iterator_destroy(&mem_iter);
01439 
01440    ao2_unlock(q);
01441    return -1;
01442 }

static int get_queue_member_status ( struct member cur  )  [static]

Return the current state of a member.

Definition at line 1621 of file app_queue.c.

References ast_extension_state(), ast_strlen_zero(), extensionstate2devicestate(), member::state_context, member::state_exten, and member::state_interface.

Referenced by create_queue_member(), kill_dead_members(), and ring_entry().

01622 {
01623    return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
01624 }

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

Definition at line 7869 of file app_queue.c.

References add_to_queue(), ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), ast_strlen_zero(), 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.

07870 {
07871    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07872    int penalty;
07873 
07874    switch ( cmd ) {
07875    case CLI_INIT:
07876       e->command = "queue add member";
07877       e->usage =
07878          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07879          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
07880       return NULL;
07881    case CLI_GENERATE:
07882       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07883    }
07884 
07885    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07886       return CLI_SHOWUSAGE;
07887    } else if (strcmp(a->argv[4], "to")) {
07888       return CLI_SHOWUSAGE;
07889    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07890       return CLI_SHOWUSAGE;
07891    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07892       return CLI_SHOWUSAGE;
07893    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07894       return CLI_SHOWUSAGE;
07895    }
07896 
07897    queuename = a->argv[5];
07898    interface = a->argv[3];
07899    if (a->argc >= 8) {
07900       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07901          if (penalty < 0) {
07902             ast_cli(a->fd, "Penalty must be >= 0\n");
07903             penalty = 0;
07904          }
07905       } else {
07906          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07907          penalty = 0;
07908       }
07909    } else {
07910       penalty = 0;
07911    }
07912 
07913    if (a->argc >= 10) {
07914       membername = a->argv[9];
07915    }
07916 
07917    if (a->argc >= 12) {
07918       state_interface = a->argv[11];
07919    }
07920 
07921    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07922    case RES_OKAY:
07923       if (ast_strlen_zero(membername) || !log_membername_as_agent) {
07924          ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
07925       } else {
07926          ast_queue_log(queuename, "CLI", membername, "ADDMEMBER", "%s", "");
07927       }
07928       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
07929       return CLI_SUCCESS;
07930    case RES_EXISTS:
07931       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
07932       return CLI_FAILURE;
07933    case RES_NOSUCHQUEUE:
07934       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
07935       return CLI_FAILURE;
07936    case RES_OUTOFMEMORY:
07937       ast_cli(a->fd, "Out of memory\n");
07938       return CLI_FAILURE;
07939    case RES_NOT_DYNAMIC:
07940       ast_cli(a->fd, "Member not dynamic\n");
07941       return CLI_FAILURE;
07942    default:
07943       return CLI_FAILURE;
07944    }
07945 }

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

Definition at line 8068 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, member::interface, ast_cli_args::line, ast_cli_args::n, member::paused, ast_cli_args::pos, RESULT_SUCCESS, set_member_paused(), ast_cli_entry::usage, and word.

08069 {
08070    const char *queuename, *interface, *reason;
08071    int paused;
08072 
08073    switch (cmd) {
08074    case CLI_INIT:
08075       e->command = "queue {pause|unpause} member";
08076       e->usage = 
08077          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
08078          "  Pause or unpause a queue member. Not specifying a particular queue\n"
08079          "  will pause or unpause a member across all queues to which the member\n"
08080          "  belongs.\n";
08081       return NULL;
08082    case CLI_GENERATE:
08083       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
08084    }
08085 
08086    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
08087       return CLI_SHOWUSAGE;
08088    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
08089       return CLI_SHOWUSAGE;
08090    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
08091       return CLI_SHOWUSAGE;
08092    }
08093 
08094 
08095    interface = a->argv[3];
08096    queuename = a->argc >= 6 ? a->argv[5] : NULL;
08097    reason = a->argc == 8 ? a->argv[7] : NULL;
08098    paused = !strcasecmp(a->argv[1], "pause");
08099 
08100    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
08101       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
08102       if (!ast_strlen_zero(queuename))
08103          ast_cli(a->fd, " in queue '%s'", queuename);
08104       if (!ast_strlen_zero(reason))
08105          ast_cli(a->fd, " for reason '%s'", reason);
08106       ast_cli(a->fd, "\n");
08107       return CLI_SUCCESS;
08108    } else {
08109       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
08110       if (!ast_strlen_zero(queuename))
08111          ast_cli(a->fd, " in queue '%s'", queuename);
08112       if (!ast_strlen_zero(reason))
08113          ast_cli(a->fd, " for reason '%s'", reason);
08114       ast_cli(a->fd, "\n");
08115       return CLI_FAILURE;
08116    }
08117 }

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

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

08278 {
08279    struct ast_flags mask = {0,};
08280    int i;
08281 
08282    switch (cmd) {
08283       case CLI_INIT:
08284          e->command = "queue reload {parameters|members|rules|all}";
08285          e->usage =
08286             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
08287             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
08288             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
08289             "specified in order to know what information to reload. Below is an explanation\n"
08290             "of each of these qualifiers.\n"
08291             "\n"
08292             "\t'members' - reload queue members from queues.conf\n"
08293             "\t'parameters' - reload all queue options except for queue members\n"
08294             "\t'rules' - reload the queuerules.conf file\n"
08295             "\t'all' - reload queue rules, parameters, and members\n"
08296             "\n"
08297             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
08298             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
08299             "one queue is specified when using this command, reloading queue rules may cause\n"
08300             "other queues to be affected\n";
08301          return NULL;
08302       case CLI_GENERATE:
08303          if (a->pos >= 3) {
08304             return complete_queue(a->line, a->word, a->pos, a->n);
08305          } else {
08306             return NULL;
08307          }
08308    }
08309 
08310    if (a->argc < 3)
08311       return CLI_SHOWUSAGE;
08312 
08313    if (!strcasecmp(a->argv[2], "rules")) {
08314       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
08315    } else if (!strcasecmp(a->argv[2], "members")) {
08316       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
08317    } else if (!strcasecmp(a->argv[2], "parameters")) {
08318       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
08319    } else if (!strcasecmp(a->argv[2], "all")) {
08320       ast_set_flag(&mask, AST_FLAGS_ALL);
08321    }
08322 
08323    if (a->argc == 3) {
08324       reload_handler(1, &mask, NULL);
08325       return CLI_SUCCESS;
08326    }
08327 
08328    for (i = 3; i < a->argc; ++i) {
08329       reload_handler(1, &mask, a->argv[i]);
08330    }
08331 
08332    return CLI_SUCCESS;
08333 }

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

Definition at line 7992 of file app_queue.c.

References ao2_ref, ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_remove_member(), ast_cli_args::fd, find_member_by_queuename_and_interface(), ast_cli_args::line, member::membername, 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.

07993 {
07994    const char *queuename, *interface;
07995    struct member *mem = NULL;
07996 
07997    switch (cmd) {
07998    case CLI_INIT:
07999       e->command = "queue remove member";
08000       e->usage =
08001          "Usage: queue remove member <channel> from <queue>\n"
08002          "       Remove a specific channel from a queue.\n";
08003       return NULL;
08004    case CLI_GENERATE:
08005       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
08006    }
08007 
08008    if (a->argc != 6) {
08009       return CLI_SHOWUSAGE;
08010    } else if (strcmp(a->argv[4], "from")) {
08011       return CLI_SHOWUSAGE;
08012    }
08013 
08014    queuename = a->argv[5];
08015    interface = a->argv[3];
08016 
08017    switch (remove_from_queue(queuename, interface)) {
08018    case RES_OKAY:
08019       if (log_membername_as_agent) {
08020          mem = find_member_by_queuename_and_interface(queuename, interface);
08021       }
08022       if (!mem || ast_strlen_zero(mem->membername)) {
08023          ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
08024       } else {
08025          ast_queue_log(queuename, "CLI", mem->membername, "REMOVEMEMBER", "%s", "");
08026       }
08027       if (mem) {
08028          ao2_ref(mem, -1);
08029       }
08030       ast_cli(a->fd, "Removed interface %s from queue '%s'\n", interface, queuename);
08031       return CLI_SUCCESS;
08032    case RES_EXISTS:
08033       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
08034       return CLI_FAILURE;
08035    case RES_NOSUCHQUEUE:
08036       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
08037       return CLI_FAILURE;
08038    case RES_OUTOFMEMORY:
08039       ast_cli(a->fd, "Out of memory\n");
08040       return CLI_FAILURE;
08041    case RES_NOT_DYNAMIC:
08042       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
08043       return CLI_FAILURE;
08044    default:
08045       return CLI_FAILURE;
08046    }
08047 }

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

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

08239 {
08240    struct ast_flags mask = {QUEUE_RESET_STATS,};
08241    int i;
08242 
08243    switch (cmd) {
08244       case CLI_INIT:
08245          e->command = "queue reset stats";
08246          e->usage =
08247             "Usage: queue reset stats [<queuenames>]\n"
08248             "\n"
08249             "Issuing this command will reset statistics for\n"
08250             "<queuenames>, or for all queues if no queue is\n"
08251             "specified.\n";
08252          return NULL;
08253       case CLI_GENERATE:
08254          if (a->pos >= 3) {
08255             return complete_queue(a->line, a->word, a->pos, a->n);
08256          } else {
08257             return NULL;
08258          }
08259    }
08260 
08261    if (a->argc < 3) {
08262       return CLI_SHOWUSAGE;
08263    }
08264 
08265    if (a->argc == 3) {
08266       reload_handler(1, &mask, NULL);
08267       return CLI_SUCCESS;
08268    }
08269 
08270    for (i = 3; i < a->argc; ++i) {
08271       reload_handler(1, &mask, a->argv[i]);
08272    }
08273 
08274    return CLI_SUCCESS;
08275 }

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

Definition at line 8204 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), 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, rule_list::rules, penalty_rule::time, ast_cli_entry::usage, and ast_cli_args::word.

08205 {
08206    const char *rule;
08207    struct rule_list *rl_iter;
08208    struct penalty_rule *pr_iter;
08209    switch (cmd) {
08210    case CLI_INIT:
08211       e->command = "queue show rules";
08212       e->usage =
08213       "Usage: queue show rules [rulename]\n"
08214       "  Show the list of rules associated with rulename. If no\n"
08215       "  rulename is specified, list all rules defined in queuerules.conf\n";
08216       return NULL;
08217    case CLI_GENERATE:
08218       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
08219    }
08220 
08221    if (a->argc != 3 && a->argc != 4)
08222       return CLI_SHOWUSAGE;
08223 
08224    rule = a->argc == 4 ? a->argv[3] : "";
08225    AST_LIST_LOCK(&rule_lists);
08226    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
08227       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
08228          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
08229          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
08230             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);
08231          }
08232       }
08233    }
08234    AST_LIST_UNLOCK(&rule_lists);
08235    return CLI_SUCCESS; 
08236 }

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

Definition at line 8142 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, member::interface, ast_cli_args::line, ast_cli_args::n, member::penalty, ast_cli_args::pos, RESULT_FAILURE, RESULT_SUCCESS, set_member_penalty(), ast_cli_entry::usage, and ast_cli_args::word.

08143 {
08144    const char *queuename = NULL, *interface;
08145    int penalty = 0;
08146 
08147    switch (cmd) {
08148    case CLI_INIT:
08149       e->command = "queue set penalty";
08150       e->usage = 
08151       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
08152       "  Set a member's penalty in the queue specified. If no queue is specified\n"
08153       "  then that interface's penalty is set in all queues to which that interface is a member\n";
08154       return NULL;
08155    case CLI_GENERATE:
08156       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
08157    }
08158 
08159    if (a->argc != 6 && a->argc != 8) {
08160       return CLI_SHOWUSAGE;
08161    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
08162       return CLI_SHOWUSAGE;
08163    }
08164 
08165    if (a->argc == 8)
08166       queuename = a->argv[7];
08167    interface = a->argv[5];
08168    penalty = atoi(a->argv[3]);
08169 
08170    switch (set_member_penalty(queuename, interface, penalty)) {
08171    case RESULT_SUCCESS:
08172       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08173       return CLI_SUCCESS;
08174    case RESULT_FAILURE:
08175       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
08176       return CLI_FAILURE;
08177    default:
08178       return CLI_FAILURE;
08179    }
08180 }

static int handle_statechange ( void *  datap  )  [static]

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

Definition at line 1481 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, statechange::state, member::state_interface, and update_status().

Referenced by device_state_cb().

01482 {
01483    struct statechange *sc = datap;
01484    struct ao2_iterator miter, qiter;
01485    struct member *m;
01486    struct call_queue *q;
01487    char interface[80], *slash_pos;
01488    int found = 0;
01489 
01490    qiter = ao2_iterator_init(queues, 0);
01491    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01492       ao2_lock(q);
01493 
01494       miter = ao2_iterator_init(q->members, 0);
01495       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01496          ast_copy_string(interface, m->state_interface, sizeof(interface));
01497 
01498          if ((slash_pos = strchr(interface, '/')))
01499             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01500                *slash_pos = '\0';
01501 
01502          if (!strcasecmp(interface, sc->dev)) {
01503             found = 1;
01504             update_status(q, m, sc->state);
01505             ao2_ref(m, -1);
01506             break;
01507          }
01508       }
01509       ao2_iterator_destroy(&miter);
01510 
01511       ao2_unlock(q);
01512       queue_t_unref(q, "Done with iterator");
01513    }
01514    ao2_iterator_destroy(&qiter);
01515 
01516    if (found)
01517       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01518    else
01519       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));
01520 
01521    ast_free(sc);
01522    return 0;
01523 }

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

02916 {
02917    struct callattempt *oo;
02918 
02919    while (outgoing) {
02920       /* If someone else answered the call we should indicate this in the CANCEL */
02921       /* Hangup any existing lines we have open */
02922       if (outgoing->chan && (outgoing->chan != exception)) {
02923          if (exception || cancel_answered_elsewhere)
02924             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02925          ast_hangup(outgoing->chan);
02926       }
02927       oo = outgoing;
02928       outgoing = outgoing->q_next;
02929       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
02930       callattempt_free(oo);
02931    }
02932 }

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

References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::autopause, call_queue::autopausedelay, 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::rules, 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().

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

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

References call_queue::head, queue_ent::next, and queue_ref().

Referenced by join_queue().

01345 {
01346    struct queue_ent *cur;
01347 
01348    if (!q || !new)
01349       return;
01350    if (prev) {
01351       cur = prev->next;
01352       prev->next = new;
01353    } else {
01354       cur = q->head;
01355       q->head = new;
01356    }
01357    new->next = cur;
01358 
01359    /* every queue_ent must have a reference to it's parent call_queue, this
01360     * reference does not go away until the end of the queue_ent's life, meaning
01361     * that even when the queue_ent leaves the call_queue this ref must remain. */
01362    queue_ref(q);
01363    new->parent = q;
01364    new->pos = ++(*pos);
01365    new->opos = *pos;
01366 }

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 1807 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, rule_list::rules, and penalty_rule::time.

Referenced by reload_queue_rules().

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

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

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

01237 {
01238    int x;
01239 
01240    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01241       if (strategy == strategies[x].strategy)
01242          return strategies[x].name;
01243    }
01244 
01245    return "<unknown>";
01246 }

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

Definition at line 5226 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(), queue_function_mem_read(), queue_function_mem_write(), rna(), set_member_paused(), and set_member_penalty_help_members().

05227 {
05228    struct member *mem;
05229    struct ao2_iterator mem_iter;
05230 
05231    if (!q) {
05232       return NULL;
05233    }
05234    mem_iter = ao2_iterator_init(q->members, 0);
05235    while ((mem = ao2_iterator_next(&mem_iter))) {
05236       if (!strcasecmp(interface, mem->interface)) {
05237          ao2_iterator_destroy(&mem_iter);
05238          return mem;
05239       }
05240       ao2_ref(mem, -1);
05241    }
05242    ao2_iterator_destroy(&mem_iter);
05243 
05244    return NULL;
05245 }

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

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

Referenced by queue_exec(), and wait_our_turn().

03966 {
03967    struct queue_ent *ch;
03968    int res;
03969    int avl;
03970    int idx = 0;
03971    /* This needs a lock. How many members are available to be served? */
03972    ao2_lock(qe->parent);
03973 
03974    avl = num_available_members(qe->parent);
03975 
03976    ch = qe->parent->head;
03977 
03978    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03979 
03980    while ((idx < avl) && (ch) && (ch != qe)) {
03981       if (!ch->pending)
03982          idx++;
03983       ch = ch->next;       
03984    }
03985 
03986    ao2_unlock(qe->parent);
03987    /* If the queue entry is within avl [the number of available members] calls from the top ... 
03988     * Autofill and position check added to support autofill=no (as only calls
03989     * from the front of the queue are valid when autofill is disabled)
03990     */
03991    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03992       ast_debug(1, "It's our turn (%s).\n", ast_channel_name(qe->chan));
03993       res = 1;
03994    } else {
03995       ast_debug(1, "It's not our turn (%s).\n", ast_channel_name(qe->chan));
03996       res = 0;
03997    }
03998 
03999    return res;
04000 }

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

Definition at line 2524 of file app_queue.c.

References call_queue::announce, queue_ent::announce, ao2_lock, ao2_unlock, ast_channel_name(), ast_channel_uniqueid(), ast_copy_string(), ast_debug, ast_log(), ast_manager_event, ast_channel::caller, queue_ent::chan, ast_channel::connected, call_queue::context, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, find_load_queue_rt_friendly(), get_member_status(), call_queue::head, ast_party_connected_line::id, ast_party_caller::id, insert_entry(), call_queue::joinempty, LOG_NOTICE, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, call_queue::moh, queue_ent::moh, call_queue::name, ast_party_id::name, queue_ent::next, 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().

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

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

Definition at line 6937 of file app_queue.c.

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

Referenced by reload_single_queue().

06938 {
06939    struct member *member = obj;
06940 
06941    if (!member->delme) {
06942       member->status = get_queue_member_status(member);
06943       return 0;
06944    } else {
06945       return CMP_MATCH;
06946    }
06947 }

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

Definition at line 7066 of file app_queue.c.

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

Referenced by reload_queues().

07067 {
07068    struct call_queue *q = obj;
07069    char *queuename = arg;
07070    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
07071       return CMP_MATCH;
07072    } else {
07073       return 0;
07074    }
07075 }

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

References ao2_lock, ao2_unlock, ast_channel_name(), ast_channel_uniqueid(), 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, call_queue::name, queue_ent::next, queue_ent::parent, pbx_builtin_setvar_helper(), queue_ent::pos, queue_ent::qe_rules, 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().

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

static int load_module ( void   )  [static]

Definition at line 8680 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(), cli_queue, 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_data_providers, queue_exec(), queue_hash_cb(), queueexists_function, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, queuewaitingcount_function, reload_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().

08681 {
08682    int res;
08683    struct ast_context *con;
08684    struct ast_flags mask = {AST_FLAGS_ALL, };
08685 
08686    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08687 
08688    use_weight = 0;
08689 
08690    if (reload_handler(0, &mask, NULL))
08691       return AST_MODULE_LOAD_DECLINE;
08692 
08693    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08694    if (!con)
08695       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08696    else
08697       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08698 
08699    if (queue_persistent_members)
08700       reload_queue_members();
08701 
08702    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08703 
08704    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08705    res = ast_register_application_xml(app, queue_exec);
08706    res |= ast_register_application_xml(app_aqm, aqm_exec);
08707    res |= ast_register_application_xml(app_rqm, rqm_exec);
08708    res |= ast_register_application_xml(app_pqm, pqm_exec);
08709    res |= ast_register_application_xml(app_upqm, upqm_exec);
08710    res |= ast_register_application_xml(app_ql, ql_exec);
08711    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08712    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08713    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08714    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08715    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08716    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08717    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08718    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08719    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08720    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08721    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08722    res |= ast_custom_function_register(&queuevar_function);
08723    res |= ast_custom_function_register(&queueexists_function);
08724    res |= ast_custom_function_register(&queuemembercount_function);
08725    res |= ast_custom_function_register(&queuemembercount_dep);
08726    res |= ast_custom_function_register(&queuememberlist_function);
08727    res |= ast_custom_function_register(&queuewaitingcount_function);
08728    res |= ast_custom_function_register(&queuememberpenalty_function);
08729 
08730    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08731       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08732    }
08733 
08734    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08735    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08736       res = -1;
08737    }
08738 
08739    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08740 
08741    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08742 
08743    return res ? AST_MODULE_LOAD_DECLINE : 0;
08744 }

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

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

07620 {
07621    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07622    int paused, penalty = 0;
07623 
07624    queuename = astman_get_header(m, "Queue");
07625    interface = astman_get_header(m, "Interface");
07626    penalty_s = astman_get_header(m, "Penalty");
07627    paused_s = astman_get_header(m, "Paused");
07628    membername = astman_get_header(m, "MemberName");
07629    state_interface = astman_get_header(m, "StateInterface");
07630 
07631    if (ast_strlen_zero(queuename)) {
07632       astman_send_error(s, m, "'Queue' not specified.");
07633       return 0;
07634    }
07635 
07636    if (ast_strlen_zero(interface)) {
07637       astman_send_error(s, m, "'Interface' not specified.");
07638       return 0;
07639    }
07640 
07641    if (ast_strlen_zero(penalty_s))
07642       penalty = 0;
07643    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07644       penalty = 0;
07645 
07646    if (ast_strlen_zero(paused_s))
07647       paused = 0;
07648    else
07649       paused = abs(ast_true(paused_s));
07650 
07651    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07652    case RES_OKAY:
07653       if (ast_strlen_zero(membername) || !log_membername_as_agent) {
07654          ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07655       } else {
07656          ast_queue_log(queuename, "MANAGER", membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
07657       }
07658       astman_send_ack(s, m, "Added interface to queue");
07659       break;
07660    case RES_EXISTS:
07661       astman_send_error(s, m, "Unable to add interface: Already there");
07662       break;
07663    case RES_NOSUCHQUEUE:
07664       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07665       break;
07666    case RES_OUTOFMEMORY:
07667       astman_send_error(s, m, "Out of memory");
07668       break;
07669    }
07670 
07671    return 0;
07672 }

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

Definition at line 7721 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member::interface, member::paused, and set_member_paused().

Referenced by load_module().

07722 {
07723    const char *queuename, *interface, *paused_s, *reason;
07724    int paused;
07725 
07726    interface = astman_get_header(m, "Interface");
07727    paused_s = astman_get_header(m, "Paused");
07728    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07729    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07730 
07731    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07732       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07733       return 0;
07734    }
07735 
07736    paused = abs(ast_true(paused_s));
07737 
07738    if (set_member_paused(queuename, interface, reason, paused))
07739       astman_send_error(s, m, "Interface not found");
07740    else
07741       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07742    return 0;
07743 }

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

Definition at line 7745 of file app_queue.c.

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

Referenced by load_module().

07746 {
07747    const char *queuename, *event, *message, *interface, *uniqueid;
07748 
07749    queuename = astman_get_header(m, "Queue");
07750    uniqueid = astman_get_header(m, "UniqueId");
07751    interface = astman_get_header(m, "Interface");
07752    event = astman_get_header(m, "Event");
07753    message = astman_get_header(m, "Message");
07754 
07755    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07756       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07757       return 0;
07758    }
07759 
07760    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07761    astman_send_ack(s, m, "Event added successfully");
07762 
07763    return 0;
07764 }

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

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

07845 {
07846    const char *queuename, *interface, *penalty_s;
07847    int penalty;
07848 
07849    interface = astman_get_header(m, "Interface");
07850    penalty_s = astman_get_header(m, "Penalty");
07851    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
07852    queuename = astman_get_header(m, "Queue");
07853 
07854    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07855       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07856       return 0;
07857    }
07858  
07859    penalty = atoi(penalty_s);
07860 
07861    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07862       astman_send_error(s, m, "Invalid interface, queuename or penalty");
07863    else
07864       astman_send_ack(s, m, "Interface penalty set successfully");
07865 
07866    return 0;
07867 }

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

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

07767 {
07768    struct ast_flags mask = {0,};
07769    const char *queuename = NULL;
07770    int header_found = 0;
07771 
07772    queuename = astman_get_header(m, "Queue");
07773    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07774       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07775       header_found = 1;
07776    }
07777    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07778       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07779       header_found = 1;
07780    }
07781    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07782       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07783       header_found = 1;
07784    }
07785 
07786    if (!header_found) {
07787       ast_set_flag(&mask, AST_FLAGS_ALL);
07788    }
07789 
07790    if (!reload_handler(1, &mask, queuename)) {
07791       astman_send_ack(s, m, "Queue reloaded successfully");
07792    } else {
07793       astman_send_error(s, m, "Error encountered while reloading queue");
07794    }
07795    return 0;
07796 }

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

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

07799 {
07800    const char *queuename = NULL;
07801    struct ast_flags mask = {QUEUE_RESET_STATS,};
07802    
07803    queuename = astman_get_header(m, "Queue");
07804 
07805    if (!reload_handler(1, &mask, queuename)) {
07806       astman_send_ack(s, m, "Queue stats reset successfully");
07807    } else {
07808       astman_send_error(s, m, "Error encountered while resetting queue stats");
07809    }
07810    return 0;
07811 }

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

Definition at line 7407 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, rule_list::rules, and penalty_rule::time.

Referenced by load_module().

07408 {
07409    const char *rule = astman_get_header(m, "Rule");
07410    const char *id = astman_get_header(m, "ActionID");
07411    struct rule_list *rl_iter;
07412    struct penalty_rule *pr_iter;
07413 
07414    astman_append(s, "Response: Success\r\n");
07415    if (!ast_strlen_zero(id)) {
07416       astman_append(s, "ActionID: %s\r\n", id);
07417    }
07418 
07419    AST_LIST_LOCK(&rule_lists);
07420    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07421       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07422          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07423          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07424             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 );
07425          }
07426          if (!ast_strlen_zero(rule))
07427             break;
07428       }
07429    }
07430    AST_LIST_UNLOCK(&rule_lists);
07431 
07432    /*
07433     * Two blank lines instead of one because the Response and
07434     * ActionID headers used to not be present.
07435     */
07436    astman_append(s, "\r\n\r\n");
07437 
07438    return RESULT_SUCCESS;
07439 }

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

Definition at line 7397 of file app_queue.c.

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

Referenced by load_module().

07398 {
07399    static const char * const a[] = { "queue", "show" };
07400 
07401    __queues_show(s, -1, 2, a);
07402    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07403 
07404    return RESULT_SUCCESS;
07405 }

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

Queue status info via AMI.

Definition at line 7517 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_channel_name(), ast_channel_uniqueid(), 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, call_queue::name, queue_ent::next, 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::state_interface, 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().

07518 {
07519    time_t now;
07520    int pos;
07521    const char *id = astman_get_header(m,"ActionID");
07522    const char *queuefilter = astman_get_header(m,"Queue");
07523    const char *memberfilter = astman_get_header(m,"Member");
07524    char idText[256] = "";
07525    struct call_queue *q;
07526    struct queue_ent *qe;
07527    float sl = 0;
07528    struct member *mem;
07529    struct ao2_iterator queue_iter;
07530    struct ao2_iterator mem_iter;
07531 
07532    astman_send_ack(s, m, "Queue status will follow");
07533    time(&now);
07534    if (!ast_strlen_zero(id))
07535       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07536 
07537    queue_iter = ao2_iterator_init(queues, 0);
07538    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07539       ao2_lock(q);
07540 
07541       /* List queue properties */
07542       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07543          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07544          astman_append(s, "Event: QueueParams\r\n"
07545             "Queue: %s\r\n"
07546             "Max: %d\r\n"
07547             "Strategy: %s\r\n"
07548             "Calls: %d\r\n"
07549             "Holdtime: %d\r\n"
07550             "TalkTime: %d\r\n"
07551             "Completed: %d\r\n"
07552             "Abandoned: %d\r\n"
07553             "ServiceLevel: %d\r\n"
07554             "ServicelevelPerf: %2.1f\r\n"
07555             "Weight: %d\r\n"
07556             "%s"
07557             "\r\n",
07558             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07559             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07560          /* List Queue Members */
07561          mem_iter = ao2_iterator_init(q->members, 0);
07562          while ((mem = ao2_iterator_next(&mem_iter))) {
07563             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07564                astman_append(s, "Event: QueueMember\r\n"
07565                   "Queue: %s\r\n"
07566                   "Name: %s\r\n"
07567                   "Location: %s\r\n"
07568                   "StateInterface: %s\r\n"
07569                   "Membership: %s\r\n"
07570                   "Penalty: %d\r\n"
07571                   "CallsTaken: %d\r\n"
07572                   "LastCall: %d\r\n"
07573                   "Status: %d\r\n"
07574                   "Paused: %d\r\n"
07575                   "%s"
07576                   "\r\n",
07577                   q->name, mem->membername, mem->interface, mem->state_interface, mem->dynamic ? "dynamic" : "static",
07578                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07579             }
07580             ao2_ref(mem, -1);
07581          }
07582          ao2_iterator_destroy(&mem_iter);
07583          /* List Queue Entries */
07584          pos = 1;
07585          for (qe = q->head; qe; qe = qe->next) {
07586             astman_append(s, "Event: QueueEntry\r\n"
07587                "Queue: %s\r\n"
07588                "Position: %d\r\n"
07589                "Channel: %s\r\n"
07590                "Uniqueid: %s\r\n"
07591                "CallerIDNum: %s\r\n"
07592                "CallerIDName: %s\r\n"
07593                "ConnectedLineNum: %s\r\n"
07594                "ConnectedLineName: %s\r\n"
07595                "Wait: %ld\r\n"
07596                "%s"
07597                "\r\n",
07598                q->name, pos++, ast_channel_name(qe->chan), ast_channel_uniqueid(qe->chan),
07599                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07600                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07601                S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
07602                S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
07603                (long) (now - qe->start), idText);
07604          }
07605       }
07606       ao2_unlock(q);
07607       queue_t_unref(q, "Done with iterator");
07608    }
07609    ao2_iterator_destroy(&queue_iter);
07610 
07611    astman_append(s,
07612       "Event: QueueStatusComplete\r\n"
07613       "%s"
07614       "\r\n",idText);
07615 
07616    return RESULT_SUCCESS;
07617 }

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

Summary of queue info via the AMI.

Definition at line 7442 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_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), call_queue::head, call_queue::holdtime, call_queue::members, call_queue::name, queue_ent::next, member::paused, queue_t_unref, queues, RESULT_SUCCESS, queue_ent::start, member::status, and call_queue::talktime.

Referenced by load_module().

07443 {
07444    time_t now;
07445    int qmemcount = 0;
07446    int qmemavail = 0;
07447    int qchancount = 0;
07448    int qlongestholdtime = 0;
07449    const char *id = astman_get_header(m, "ActionID");
07450    const char *queuefilter = astman_get_header(m, "Queue");
07451    char idText[256] = "";
07452    struct call_queue *q;
07453    struct queue_ent *qe;
07454    struct member *mem;
07455    struct ao2_iterator queue_iter;
07456    struct ao2_iterator mem_iter;
07457 
07458    astman_send_ack(s, m, "Queue summary will follow");
07459    time(&now);
07460    if (!ast_strlen_zero(id))
07461       snprintf(idText, 256, "ActionID: %s\r\n", id);
07462    queue_iter = ao2_iterator_init(queues, 0);
07463    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07464       ao2_lock(q);
07465 
07466       /* List queue properties */
07467       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07468          /* Reset the necessary local variables if no queuefilter is set*/
07469          qmemcount = 0;
07470          qmemavail = 0;
07471          qchancount = 0;
07472          qlongestholdtime = 0;
07473 
07474          /* List Queue Members */
07475          mem_iter = ao2_iterator_init(q->members, 0);
07476          while ((mem = ao2_iterator_next(&mem_iter))) {
07477             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07478                ++qmemcount;
07479                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
07480                   ++qmemavail;
07481                }
07482             }
07483             ao2_ref(mem, -1);
07484          }
07485          ao2_iterator_destroy(&mem_iter);
07486          for (qe = q->head; qe; qe = qe->next) {
07487             if ((now - qe->start) > qlongestholdtime) {
07488                qlongestholdtime = now - qe->start;
07489             }
07490             ++qchancount;
07491          }
07492          astman_append(s, "Event: QueueSummary\r\n"
07493             "Queue: %s\r\n"
07494             "LoggedIn: %d\r\n"
07495             "Available: %d\r\n"
07496             "Callers: %d\r\n" 
07497             "HoldTime: %d\r\n"
07498             "TalkTime: %d\r\n"
07499             "LongestHoldTime: %d\r\n"
07500             "%s"
07501             "\r\n",
07502             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07503       }
07504       ao2_unlock(q);
07505       queue_t_unref(q, "Done with iterator");
07506    }
07507    ao2_iterator_destroy(&queue_iter);
07508    astman_append(s,
07509       "Event: QueueSummaryComplete\r\n"
07510       "%s"
07511       "\r\n", idText);
07512 
07513    return RESULT_SUCCESS;
07514 }

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

Definition at line 7674 of file app_queue.c.

References ao2_ref, ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), find_member_by_queuename_and_interface(), member::membername, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

07675 {
07676    const char *queuename, *interface;
07677    struct member *mem = NULL;
07678 
07679    queuename = astman_get_header(m, "Queue");
07680    interface = astman_get_header(m, "Interface");
07681 
07682    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07683       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07684       return 0;
07685    }
07686 
07687    if (log_membername_as_agent) {
07688       mem = find_member_by_queuename_and_interface(queuename, interface);
07689    }
07690 
07691    switch (remove_from_queue(queuename, interface)) {
07692    case RES_OKAY:
07693       if (!mem || ast_strlen_zero(mem->membername)) {
07694          ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07695       } else {
07696          ast_queue_log(queuename, "MANAGER", mem->membername, "REMOVEMEMBER", "%s", "");
07697       }
07698       astman_send_ack(s, m, "Removed interface from queue");
07699       break;
07700    case RES_EXISTS:
07701       astman_send_error(s, m, "Unable to remove interface: Not there");
07702       break;
07703    case RES_NOSUCHQUEUE:
07704       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07705       break;
07706    case RES_OUTOFMEMORY:
07707       astman_send_error(s, m, "Out of memory");
07708       break;
07709    case RES_NOT_DYNAMIC:
07710       astman_send_error(s, m, "Member not dynamic");
07711       break;
07712    }
07713 
07714    if (mem) {
07715       ao2_ref(mem, -1);
07716    }
07717 
07718    return 0;
07719 }

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

Definition at line 7055 of file app_queue.c.

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

Referenced by reload_queues().

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

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

Definition at line 6928 of file app_queue.c.

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

Referenced by reload_single_queue().

06929 {
06930    struct member *member = obj;
06931    if (!member->dynamic) {
06932       member->delme = 1;
06933    }
06934    return 0;
06935 }

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

Definition at line 1688 of file app_queue.c.

References CMP_MATCH, CMP_STOP, member::interface, and OBJ_KEY.

Referenced by init_queue().

01689 {
01690    struct member *mem1 = obj1;
01691    struct member *mem2 = obj2;
01692    const char *interface = (flags & OBJ_KEY) ? obj2 : mem2->interface;
01693 
01694    return strcasecmp(mem1->interface, interface) ? 0 : CMP_MATCH | CMP_STOP;
01695 }

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

Definition at line 1672 of file app_queue.c.

References compress_char(), member::interface, and OBJ_KEY.

Referenced by init_queue().

01673 {
01674    const struct member *mem = obj;
01675    const char *interface = (flags & OBJ_KEY) ? obj : mem->interface;
01676    const char *chname = strchr(interface, '/');
01677    int ret = 0, i;
01678 
01679    if (!chname) {
01680       chname = interface;
01681    }
01682    for (i = 0; i < 5 && chname[i]; i++) {
01683       ret += compress_char(chname[i]) << (i * 6);
01684    }
01685    return ret;
01686 }

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, AST_DEVICE_BUSY, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, call_queue::autofill, member::ignorebusy, 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().

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

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

Definition at line 1877 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, QUEUE_EMPTY_WRAPUP, and strsep().

Referenced by queue_set_param().

01878 {
01879    char *value_copy = ast_strdupa(value);
01880    char *option = NULL;
01881    while ((option = strsep(&value_copy, ","))) {
01882       if (!strcasecmp(option, "paused")) {
01883          *empty |= QUEUE_EMPTY_PAUSED;
01884       } else if (!strcasecmp(option, "penalty")) {
01885          *empty |= QUEUE_EMPTY_PENALTY;
01886       } else if (!strcasecmp(option, "inuse")) {
01887          *empty |= QUEUE_EMPTY_INUSE;
01888       } else if (!strcasecmp(option, "ringing")) {
01889          *empty |= QUEUE_EMPTY_RINGING;
01890       } else if (!strcasecmp(option, "invalid")) {
01891          *empty |= QUEUE_EMPTY_INVALID;
01892       } else if (!strcasecmp(option, "wrapup")) {
01893          *empty |= QUEUE_EMPTY_WRAPUP;
01894       } else if (!strcasecmp(option, "unavailable")) {
01895          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01896       } else if (!strcasecmp(option, "unknown")) {
01897          *empty |= QUEUE_EMPTY_UNKNOWN;
01898       } else if (!strcasecmp(option, "loose")) {
01899          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01900       } else if (!strcasecmp(option, "strict")) {
01901          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01902       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01903          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01904       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01905          *empty = 0;
01906       } else {
01907          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01908       }
01909    }
01910 }

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

Definition at line 2611 of file app_queue.c.

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

02612 {
02613    int res;
02614 
02615    if (ast_strlen_zero(filename)) {
02616       return 0;
02617    }
02618 
02619    if (!ast_fileexists(filename, NULL, ast_channel_language(chan))) {
02620       return 0;
02621    }
02622 
02623    ast_stopstream(chan);
02624 
02625    res = ast_streamfile(chan, filename, ast_channel_language(chan));
02626    if (!res)
02627       res = ast_waitstream(chan, AST_DIGIT_ANY);
02628 
02629    ast_stopstream(chan);
02630 
02631    return res;
02632 }

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

PauseQueueMember application.

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

05707 {
05708    char *parse;
05709    AST_DECLARE_APP_ARGS(args,
05710       AST_APP_ARG(queuename);
05711       AST_APP_ARG(interface);
05712       AST_APP_ARG(options);
05713       AST_APP_ARG(reason);
05714    );
05715 
05716    if (ast_strlen_zero(data)) {
05717       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05718       return -1;
05719    }
05720 
05721    parse = ast_strdupa(data);
05722 
05723    AST_STANDARD_APP_ARGS(args, parse);
05724 
05725    if (ast_strlen_zero(args.interface)) {
05726       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05727       return -1;
05728    }
05729 
05730    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05731       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05732       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05733       return 0;
05734    }
05735 
05736    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05737 
05738    return 0;
05739 }

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

QueueLog application.

Definition at line 5916 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, member::membername, and parse().

Referenced by load_module().

05917 {
05918    char *parse;
05919 
05920    AST_DECLARE_APP_ARGS(args,
05921       AST_APP_ARG(queuename);
05922       AST_APP_ARG(uniqueid);
05923       AST_APP_ARG(membername);
05924       AST_APP_ARG(event);
05925       AST_APP_ARG(params);
05926    );
05927 
05928    if (ast_strlen_zero(data)) {
05929       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
05930       return -1;
05931    }
05932 
05933    parse = ast_strdupa(data);
05934 
05935    AST_STANDARD_APP_ARGS(args, parse);
05936 
05937    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
05938        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
05939       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
05940       return -1;
05941    }
05942 
05943    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
05944       "%s", args.params ? args.params : "");
05945 
05946    return 0;
05947 }

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

Definition at line 1287 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

01288 {
01289    struct call_queue *q = obj, *q2 = arg;
01290    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
01291 }

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

References call_queue::announcefrequency, ao2_container_count(), args, AST_APP_ARG, ast_channel_lock, ast_channel_name(), ast_channel_uniqueid(), 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, call_queue::name, ast_party_id::number, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, 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, penalty_rule::time, 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().

05991 {
05992    int res=-1;
05993    int ringing=0;
05994    const char *user_priority;
05995    const char *max_penalty_str;
05996    const char *min_penalty_str;
05997    int prio;
05998    int qcontinue = 0;
05999    int max_penalty, min_penalty;
06000    enum queue_result reason = QUEUE_UNKNOWN;
06001    /* whether to exit Queue application after the timeout hits */
06002    int tries = 0;
06003    int noption = 0;
06004    char *parse;
06005    int makeannouncement = 0;
06006    int position = 0;
06007    AST_DECLARE_APP_ARGS(args,
06008       AST_APP_ARG(queuename);
06009       AST_APP_ARG(options);
06010       AST_APP_ARG(url);
06011       AST_APP_ARG(announceoverride);
06012       AST_APP_ARG(queuetimeoutstr);
06013       AST_APP_ARG(agi);
06014       AST_APP_ARG(macro);
06015       AST_APP_ARG(gosub);
06016       AST_APP_ARG(rule);
06017       AST_APP_ARG(position);
06018    );
06019    /* Our queue entry */
06020    struct queue_ent qe = { 0 };
06021    
06022    if (ast_strlen_zero(data)) {
06023       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
06024       return -1;
06025    }
06026    
06027    parse = ast_strdupa(data);
06028    AST_STANDARD_APP_ARGS(args, parse);
06029 
06030    /* Setup our queue entry */
06031    qe.start = time(NULL);
06032 
06033    /* set the expire time based on the supplied timeout; */
06034    if (!ast_strlen_zero(args.queuetimeoutstr))
06035       qe.expire = qe.start + atoi(args.queuetimeoutstr);
06036    else
06037       qe.expire = 0;
06038 
06039    /* Get the priority from the variable ${QUEUE_PRIO} */
06040    ast_channel_lock(chan);
06041    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
06042    if (user_priority) {
06043       if (sscanf(user_priority, "%30d", &prio) == 1) {
06044          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", ast_channel_name(chan), prio);
06045       } else {
06046          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
06047             user_priority, ast_channel_name(chan));
06048          prio = 0;
06049       }
06050    } else {
06051       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
06052       prio = 0;
06053    }
06054 
06055    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
06056 
06057    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
06058       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
06059          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", ast_channel_name(chan), max_penalty);
06060       } else {
06061          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
06062             max_penalty_str, ast_channel_name(chan));
06063          max_penalty = 0;
06064       }
06065    } else {
06066       max_penalty = 0;
06067    }
06068 
06069    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
06070       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
06071          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", ast_channel_name(chan), min_penalty);
06072       } else {
06073          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
06074             min_penalty_str, ast_channel_name(chan));
06075          min_penalty = 0;
06076       }
06077    } else {
06078       min_penalty = 0;
06079    }
06080    ast_channel_unlock(chan);
06081 
06082    if (args.options && (strchr(args.options, 'r')))
06083       ringing = 1;
06084 
06085    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
06086       qe.ring_when_ringing = 1;
06087    }
06088 
06089    if (args.options && (strchr(args.options, 'c')))
06090       qcontinue = 1;
06091 
06092    if (args.position) {
06093       position = atoi(args.position);
06094       if (position < 0) {
06095          ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
06096          position = 0;
06097       }
06098    }
06099 
06100    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
06101       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
06102 
06103    qe.chan = chan;
06104    qe.prio = prio;
06105    qe.max_penalty = max_penalty;
06106    qe.min_penalty = min_penalty;
06107    qe.last_pos_said = 0;
06108    qe.last_pos = 0;
06109    qe.last_periodic_announce_time = time(NULL);
06110    qe.last_periodic_announce_sound = 0;
06111    qe.valid_digits = 0;
06112    if (join_queue(args.queuename, &qe, &reason, position)) {
06113       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
06114       set_queue_result(chan, reason);
06115       return 0;
06116    }
06117    ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ENTERQUEUE", "%s|%s|%d",
06118       S_OR(args.url, ""),
06119       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
06120       qe.opos);
06121    copy_rules(&qe, args.rule);
06122    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
06123 check_turns:
06124    if (ringing) {
06125       ast_indicate(chan, AST_CONTROL_RINGING);
06126    } else {
06127       ast_moh_start(chan, qe.moh, NULL);
06128    }
06129 
06130    /* This is the wait loop for callers 2 through maxlen */
06131    res = wait_our_turn(&qe, ringing, &reason);
06132    if (res) {
06133       goto stop;
06134    }
06135 
06136    makeannouncement = 0;
06137 
06138    for (;;) {
06139       /* This is the wait loop for the head caller*/
06140       /* To exit, they may get their call answered; */
06141       /* they may dial a digit from the queue context; */
06142       /* or, they may timeout. */
06143 
06144       /* Leave if we have exceeded our queuetimeout */
06145       if (qe.expire && (time(NULL) >= qe.expire)) {
06146          record_abandoned(&qe);
06147          reason = QUEUE_TIMEOUT;
06148          res = 0;
06149          ast_queue_log(args.queuename, ast_channel_uniqueid(chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
06150             qe.pos, qe.opos, (long) time(NULL) - qe.start);
06151          break;
06152       }
06153 
06154       if (makeannouncement) {
06155          /* Make a position announcement, if enabled */
06156          if (qe.parent->announcefrequency)
06157             if ((res = say_position(&qe,ringing)))
06158                goto stop;
06159       }
06160       makeannouncement = 1;
06161 
06162       /* Make a periodic announcement, if enabled */
06163       if (qe.parent->periodicannouncefrequency)
06164          if ((res = say_periodic_announcement(&qe,ringing)))
06165             goto stop;
06166    
06167       /* Leave if we have exceeded our queuetimeout */
06168       if (qe.expire && (time(NULL) >= qe.expire)) {
06169          record_abandoned(&qe);
06170          reason = QUEUE_TIMEOUT;
06171          res = 0;
06172          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06173          break;
06174       }
06175 
06176       /* see if we need to move to the next penalty level for this queue */
06177       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
06178          update_qe_rule(&qe);
06179       }
06180 
06181       /* Try calling all queue members for 'timeout' seconds */
06182       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
06183       if (res) {
06184          goto stop;
06185       }
06186 
06187       if (qe.parent->leavewhenempty) {
06188          int status = 0;
06189          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
06190             record_abandoned(&qe);
06191             reason = QUEUE_LEAVEEMPTY;
06192             ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
06193             res = 0;
06194             break;
06195          }
06196       }
06197 
06198       /* exit after 'timeout' cycle if 'n' option enabled */
06199       if (noption && tries >= ao2_container_count(qe.parent->members)) {
06200          ast_verb(3, "Exiting on time-out cycle\n");
06201          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
06202          record_abandoned(&qe);
06203          reason = QUEUE_TIMEOUT;
06204          res = 0;
06205          break;
06206       }
06207 
06208       
06209       /* Leave if we have exceeded our queuetimeout */
06210       if (qe.expire && (time(NULL) >= qe.expire)) {
06211          record_abandoned(&qe);
06212          reason = QUEUE_TIMEOUT;
06213          res = 0;
06214          ast_queue_log(qe.parent->name, ast_channel_uniqueid(qe.chan),"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06215          break;
06216       }
06217 
06218       /* If using dynamic realtime members, we should regenerate the member list for this queue */
06219       update_realtime_members(qe.parent);
06220       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
06221       res = wait_a_bit(&qe);
06222       if (res)
06223          goto stop;
06224 
06225       /* Since this is a priority queue and
06226        * it is not sure that we are still at the head
06227        * of the queue, go and check for our turn again.
06228        */
06229       if (!is_our_turn(&qe)) {
06230          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", ast_channel_name(qe.chan));
06231          goto check_turns;
06232       }
06233    }
06234 
06235 stop:
06236    if (res) {
06237       if (res < 0) {
06238          if (!qe.handled) {
06239             record_abandoned(&qe);
06240             ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "ABANDON",
06241                "%d|%d|%ld", qe.pos, qe.opos,
06242                (long) time(NULL) - qe.start);
06243             res = -1;
06244          } else if (qcontinue) {
06245             reason = QUEUE_CONTINUE;
06246             res = 0;
06247          }
06248       } else if (qe.valid_digits) {
06249          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), "NONE", "EXITWITHKEY",
06250             "%s|%d", qe.digits, qe.pos);
06251       }
06252    }
06253 
06254    /* Don't allow return code > 0 */
06255    if (res >= 0) {
06256       res = 0; 
06257       if (ringing) {
06258          ast_indicate(chan, -1);
06259       } else {
06260          ast_moh_stop(chan);
06261       }        
06262       ast_stopstream(chan);
06263    }
06264 
06265    set_queue_variables(qe.parent, qe.chan);
06266 
06267    leave_queue(&qe);
06268    if (reason != QUEUE_UNKNOWN)
06269       set_queue_result(chan, reason);
06270 
06271    if (qe.parent) {
06272       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
06273        * This ref must be taken away right before the queue_ent is destroyed.  In this case
06274        * the queue_ent is about to be returned on the stack */
06275       qe.parent = queue_unref(qe.parent);
06276    }
06277 
06278    return res;
06279 }

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

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

06334 {
06335    struct call_queue *q;
06336 
06337    buf[0] = '\0';
06338 
06339    if (ast_strlen_zero(data)) {
06340       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06341       return -1;
06342    }
06343    q = find_load_queue_rt_friendly(data);
06344    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06345    if (q) {
06346       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06347    }
06348 
06349    return 0;
06350 }

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

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

Get or set member properties penalty / paused / ignorebusy

Return values:
number of members (busy / free / ready / total) or member info (penalty / paused / ignorebusy)
-1 on error

Definition at line 6358 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, args, AST_APP_ARG, AST_DECLARE_APP_ARGS, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), call_queue::count, find_load_queue_rt_friendly(), member::ignorebusy, interface_exists(), member::lastcall, LOG_ERROR, LOG_WARNING, call_queue::members, member::paused, member::penalty, queue_t_unref, member::status, and call_queue::wrapuptime.

06359 {
06360    int count = 0;
06361    struct member *m;
06362    struct ao2_iterator mem_iter;
06363    struct call_queue *q;
06364 
06365    AST_DECLARE_APP_ARGS(args,
06366       AST_APP_ARG(queuename);
06367       AST_APP_ARG(option);
06368       AST_APP_ARG(interface);
06369    );
06370    /* Make sure the returned value on error is zero length string. */
06371    buf[0] = '\0';
06372 
06373    if (ast_strlen_zero(data)) {
06374       ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
06375       return -1;
06376    }
06377 
06378    AST_STANDARD_APP_ARGS(args, data);
06379 
06380    if (args.argc < 2) {
06381       ast_log(LOG_ERROR, "Missing required argument. %s(<queuename>,<option>[<interface>])\n", cmd);
06382       return -1;
06383    }
06384 
06385    if ((q = find_load_queue_rt_friendly(args.queuename))) {
06386       ao2_lock(q);
06387       if (!strcasecmp(args.option, "logged")) {
06388          mem_iter = ao2_iterator_init(q->members, 0);
06389          while ((m = ao2_iterator_next(&mem_iter))) {
06390             /* Count the agents who are logged in and presently answering calls */
06391             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06392                count++;
06393             }
06394             ao2_ref(m, -1);
06395          }
06396          ao2_iterator_destroy(&mem_iter);
06397       } else if (!strcasecmp(args.option, "free")) {
06398          mem_iter = ao2_iterator_init(q->members, 0);
06399          while ((m = ao2_iterator_next(&mem_iter))) {
06400             /* Count the agents who are logged in and presently answering calls */
06401             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06402                count++;
06403             }
06404             ao2_ref(m, -1);
06405          }
06406          ao2_iterator_destroy(&mem_iter);
06407       } else if (!strcasecmp(args.option, "ready")) {
06408          time_t now;
06409          time(&now);
06410          mem_iter = ao2_iterator_init(q->members, 0);
06411          while ((m = ao2_iterator_next(&mem_iter))) {
06412             /* Count the agents who are logged in, not paused and not wrapping up */
06413             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06414                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06415                count++;
06416             }
06417             ao2_ref(m, -1);
06418          }
06419          ao2_iterator_destroy(&mem_iter);
06420       } else if (!strcasecmp(args.option, "count") || ast_strlen_zero(args.option)) {
06421          count = ao2_container_count(q->members);
06422       } else if (!strcasecmp(args.option, "penalty") && !ast_strlen_zero(args.interface) &&
06423             ((m = interface_exists(q, args.interface)))) {
06424          count = m->penalty;
06425          ao2_ref(m, -1);
06426       } else if (!strcasecmp(args.option, "paused") && !ast_strlen_zero(args.interface) &&
06427             ((m = interface_exists(q, args.interface)))) {
06428          count = m->paused;
06429          ao2_ref(m, -1);
06430       } else if (!strcasecmp(args.option, "ignorebusy") && !ast_strlen_zero(args.interface) &&
06431             ((m = interface_exists(q, args.interface)))) {
06432          count = m->ignorebusy;
06433          ao2_ref(m, -1);
06434       } else {
06435          ast_log(LOG_ERROR, "Unknown option %s provided to %s, valid values are: "
06436             "logged, free, ready, count, penalty, paused, ignorebusy\n", args.option, cmd);
06437       }
06438       ao2_unlock(q);
06439       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06440    } else {
06441       ast_log(LOG_WARNING, "queue %s was not found\n", args.queuename);
06442    }
06443 
06444    snprintf(buf, len, "%d", count);
06445 
06446    return 0;
06447 }

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

Dialplan function QUEUE_MEMBER() Sets the members penalty / paused / ignorebusy.

Definition at line 6450 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), find_load_queue_rt_friendly(), member::ignorebusy, member::interface, interface_exists(), LOG_ERROR, call_queue::name, member::paused, member::realtime, set_member_penalty(), and update_realtime_member_field().

06451 {
06452    int memvalue;
06453    struct call_queue *q;
06454    struct member *m;
06455    char rtvalue[80];
06456 
06457    AST_DECLARE_APP_ARGS(args,
06458       AST_APP_ARG(queuename);
06459       AST_APP_ARG(option);
06460       AST_APP_ARG(interface);
06461    );
06462 
06463    if (ast_strlen_zero(data)) {
06464       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER(<queuename>,<option>,<interface>)\n");
06465       return -1;
06466    }
06467 
06468    AST_STANDARD_APP_ARGS(args, data);
06469 
06470    if (args.argc < 3) {
06471       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06472       return -1;
06473    }
06474 
06475    if (ast_strlen_zero(args.interface) && ast_strlen_zero(args.option)) {
06476       ast_log (LOG_ERROR, "<interface> and <option> parameter's can't be null\n");
06477       return -1;
06478    }
06479 
06480    memvalue = atoi(value);
06481 
06482    if (!strcasecmp(args.option, "penalty")) {
06483       /* if queuename = NULL then penalty will be set for interface in all the queues.*/
06484       if (set_member_penalty(args.queuename, args.interface, memvalue)) {
06485          ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06486          return -1;
06487       }
06488    } else if ((q = find_load_queue_rt_friendly(args.queuename))) {
06489       ao2_lock(q);
06490       if ((m = interface_exists(q, args.interface))) {
06491          sprintf(rtvalue, "%s",(memvalue <= 0) ? "0" : "1");
06492          if (!strcasecmp(args.option, "paused")) {
06493             if (m->realtime) {
06494                update_realtime_member_field(m, q->name, args.option, rtvalue);
06495             } else {
06496                m->paused = (memvalue <= 0) ? 0 : 1;
06497             }
06498          } else if (!strcasecmp(args.option, "ignorebusy")) {
06499             if (m->realtime) {
06500                update_realtime_member_field(m, q->name, args.option, rtvalue);
06501             } else {
06502                m->ignorebusy = (memvalue <= 0) ? 0 : 1;
06503             }
06504          } else {
06505             ast_log(LOG_ERROR, "Invalid option, only penalty , paused or ignorebusy are valid\n");
06506             ao2_ref(m, -1);
06507             ao2_unlock(q);
06508             ao2_ref(q, -1);
06509             return -1;
06510          }
06511          ao2_ref(m, -1);
06512       } else {
06513          ao2_unlock(q);
06514          ao2_ref(q, -1);
06515          ast_log(LOG_ERROR, "Invalid interface for queue\n");
06516          return -1;
06517       }
06518       ao2_unlock(q);
06519       ao2_ref(q, -1);
06520         } else {
06521       ast_log(LOG_ERROR, "Invalid queue\n");
06522       return -1;
06523    }
06524    return 0;
06525 }

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

Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.

Definition at line 6659 of file app_queue.c.

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

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

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

Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.

Definition at line 6690 of file app_queue.c.

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

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

static int queue_function_qac_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 6532 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(), find_load_queue_rt_friendly(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::members, queue_t_unref, and member::status.

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

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

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

Definition at line 6609 of file app_queue.c.

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

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

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

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

Definition at line 6573 of file app_queue.c.

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

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

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

create interface var with all queue details.

Return values:
0 on success
-1 on error

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

06287 {
06288    int res = -1;
06289    struct call_queue *q, tmpq = {
06290       .name = data,
06291    };
06292 
06293    char interfacevar[256] = "";
06294    float sl = 0;
06295 
06296    if (ast_strlen_zero(data)) {
06297       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06298       return -1;
06299    }
06300 
06301    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06302       ao2_lock(q);
06303       if (q->setqueuevar) {
06304          sl = 0;
06305          res = 0;
06306 
06307          if (q->callscompleted > 0) {
06308             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06309          }
06310 
06311          snprintf(interfacevar, sizeof(interfacevar),
06312             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06313             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06314 
06315          pbx_builtin_setvar_multiple(chan, interfacevar);
06316       }
06317 
06318       ao2_unlock(q);
06319       queue_t_unref(q, "Done with QUEUE() function");
06320    } else {
06321       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06322    }
06323 
06324    snprintf(buf, len, "%d", res);
06325 
06326    return 0;
06327 }

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

Definition at line 1280 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

01281 {
01282    const struct call_queue *q = obj;
01283 
01284    return ast_str_case_hash(q->name);
01285 }

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

Definition at line 1305 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01306 {
01307    ao2_ref(q, 1);
01308    return q;
01309 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06819 {
06820    const char *general_val = NULL;
06821    queue_persistent_members = 0;
06822    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) {
06823       queue_persistent_members = ast_true(general_val);
06824    }
06825    autofill_default = 0;
06826    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) {
06827       autofill_default = ast_true(general_val);
06828    }
06829    montype_default = 0;
06830    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06831       if (!strcasecmp(general_val, "mixmonitor"))
06832          montype_default = 1;
06833    }
06834    update_cdr = 0;
06835    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) {
06836       update_cdr = ast_true(general_val);
06837    }
06838    shared_lastcall = 0;
06839    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) {
06840       shared_lastcall = ast_true(general_val);
06841    }
06842    negative_penalty_invalid = 0;
06843    if ((general_val = ast_variable_retrieve(cfg, "general", "negative_penalty_invalid"))) {
06844       negative_penalty_invalid = ast_true(general_val);
06845    }
06846    log_membername_as_agent = 0;
06847    if ((general_val = ast_variable_retrieve(cfg, "general", "log_membername_as_agent"))) {
06848       log_membername_as_agent = ast_true(general_val);
06849    }
06850    check_state_unknown = 0;
06851    if ((general_val = ast_variable_retrieve(cfg, "general", "check_state_unknown"))) {
06852       check_state_unknown = ast_true(general_val);
06853    }
06854 }

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

References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, autopause2int(), call_queue::autopausebusy, call_queue::autopausedelay, call_queue::autopauseunavail, 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::name, 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, strsep(), 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().

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

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

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

07379 {
07380    switch ( cmd ) {
07381    case CLI_INIT:
07382       e->command = "queue show";
07383       e->usage =
07384          "Usage: queue show\n"
07385          "       Provides summary information on a specified queue.\n";
07386       return NULL;
07387    case CLI_GENERATE:
07388       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07389    }
07390 
07391    return __queues_show(NULL, a->fd, a->argc, a->argv);
07392 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4283 of file app_queue.c.

References ast_free.

04284 {
04285    struct queue_transfer_ds *qtds = data;
04286    ast_free(qtds);
04287 }

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

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_uniqueid(), 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, call_queue::name, queue_ent::opos, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, and update_queue().

04307 {
04308    struct queue_transfer_ds *qtds = data;
04309    struct queue_ent *qe = qtds->qe;
04310    struct member *member = qtds->member;
04311    time_t callstart = qtds->starttime;
04312    int callcompletedinsl = qtds->callcompletedinsl;
04313    struct ast_datastore *datastore;
04314 
04315    ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04316             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04317             (long) (time(NULL) - callstart), qe->opos);
04318 
04319    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04320    
04321    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04322    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04323       ast_channel_datastore_remove(old_chan, datastore);
04324    } else {
04325       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04326    }
04327 }

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

Definition at line 1311 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec(), and queues_data_provider_get().

01312 {
01313    ao2_ref(q, -1);
01314    return NULL;
01315 }

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

Definition at line 8572 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(), find_load_queue_rt_friendly(), call_queue::name, queue_unref(), queues, queues_data_provider_get_helper(), call_queue::realtime, and SENTINEL.

08574 {
08575    struct ao2_iterator i;
08576    struct call_queue *queue, *queue_realtime = NULL;
08577    struct ast_config *cfg;
08578    char *queuename;
08579 
08580    /* load realtime queues. */
08581    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08582    if (cfg) {
08583       for (queuename = ast_category_browse(cfg, NULL);
08584             !ast_strlen_zero(queuename);
08585             queuename = ast_category_browse(cfg, queuename)) {
08586          if ((queue = find_load_queue_rt_friendly(queuename))) {
08587             queue_unref(queue);
08588          }
08589       }
08590       ast_config_destroy(cfg);
08591    }
08592 
08593    /* static queues. */
08594    i = ao2_iterator_init(queues, 0);
08595    while ((queue = ao2_iterator_next(&i))) {
08596       ao2_lock(queue);
08597       if (queue->realtime) {
08598          queue_realtime = find_load_queue_rt_friendly(queue->name);
08599          if (!queue_realtime) {
08600             ao2_unlock(queue);
08601             queue_unref(queue);
08602             continue;
08603          }
08604          queue_unref(queue_realtime);
08605       }
08606 
08607       queues_data_provider_get_helper(search, data_root, queue);
08608       ao2_unlock(queue);
08609       queue_unref(queue);
08610    }
08611    ao2_iterator_destroy(&i);
08612 
08613    return 0;
08614 }

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 8466 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, queue_ent::next, and call_queue::strategy.

Referenced by queues_data_provider_get().

08468 {
08469    struct ao2_iterator im;
08470    struct member *member;
08471    struct queue_ent *qe;
08472    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08473    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08474 
08475    data_queue = ast_data_add_node(data_root, "queue");
08476    if (!data_queue) {
08477       return;
08478    }
08479 
08480    ast_data_add_structure(call_queue, data_queue, queue);
08481 
08482    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08483    ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members));
08484 
08485    /* announce position */
08486    enum_node = ast_data_add_node(data_queue, "announceposition");
08487    if (!enum_node) {
08488       return;
08489    }
08490    switch (queue->announceposition) {
08491    case ANNOUNCEPOSITION_LIMIT:
08492       ast_data_add_str(enum_node, "text", "limit");
08493       break;
08494    case ANNOUNCEPOSITION_MORE_THAN:
08495       ast_data_add_str(enum_node, "text", "more");
08496       break;
08497    case ANNOUNCEPOSITION_YES:
08498       ast_data_add_str(enum_node, "text", "yes");
08499       break;
08500    case ANNOUNCEPOSITION_NO:
08501       ast_data_add_str(enum_node, "text", "no");
08502       break;
08503    default:
08504       ast_data_add_str(enum_node, "text", "unknown");
08505       break;
08506    }
08507    ast_data_add_int(enum_node, "value", queue->announceposition);
08508 
08509    /* add queue members */
08510    im = ao2_iterator_init(queue->members, 0);
08511    while ((member = ao2_iterator_next(&im))) {
08512       if (!data_members) {
08513          data_members = ast_data_add_node(data_queue, "members");
08514          if (!data_members) {
08515             ao2_ref(member, -1);
08516             continue;
08517          }
08518       }
08519 
08520       data_member = ast_data_add_node(data_members, "member");
08521       if (!data_member) {
08522          ao2_ref(member, -1);
08523          continue;
08524       }
08525 
08526       ast_data_add_structure(member, data_member, member);
08527 
08528       ao2_ref(member, -1);
08529    }
08530    ao2_iterator_destroy(&im);
08531 
08532    /* include the callers inside the result. */
08533    if (queue->head) {
08534       for (qe = queue->head; qe; qe = qe->next) {
08535          if (!data_callers) {
08536             data_callers = ast_data_add_node(data_queue, "callers");
08537             if (!data_callers) {
08538                continue;
08539             }
08540          }
08541 
08542          data_caller = ast_data_add_node(data_callers, "caller");
08543          if (!data_caller) {
08544             continue;
08545          }
08546 
08547          ast_data_add_structure(queue_ent, data_caller, qe);
08548 
08549          /* add the caller channel. */
08550          data_caller_channel = ast_data_add_node(data_caller, "channel");
08551          if (!data_caller_channel) {
08552             continue;
08553          }
08554 
08555          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08556       }
08557    }
08558 
08559    /* if this queue doesn't match remove the added queue. */
08560    if (!ast_data_search_match(search, data_queue)) {
08561       ast_data_remove_node(data_root, data_queue);
08562    }
08563 }

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

Definition at line 2817 of file app_queue.c.

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

Referenced by try_calling().

02818 {
02819    int oldvalue;
02820 
02821    /* Calculate holdtime using an exponential average */
02822    /* Thanks to SRT for this contribution */
02823    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02824 
02825    ao2_lock(qe->parent);
02826    oldvalue = qe->parent->holdtime;
02827    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02828    ao2_unlock(qe->parent);
02829 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 3451 of file app_queue.c.

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

Referenced by queue_exec(), and try_calling().

03452 {
03453    set_queue_variables(qe->parent, qe->chan);
03454    ao2_lock(qe->parent);
03455    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03456       "Queue: %s\r\n"
03457       "Uniqueid: %s\r\n"
03458       "Position: %d\r\n"
03459       "OriginalPosition: %d\r\n"
03460       "HoldTime: %d\r\n",
03461       qe->parent->name, ast_channel_uniqueid(qe->chan), qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03462 
03463    qe->parent->callsabandoned++;
03464    ao2_unlock(qe->parent);
03465 }

static int reload ( void   )  [static]

Definition at line 8746 of file app_queue.c.

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

08747 {
08748    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08749    ast_unload_realtime("queue_members");
08750    reload_handler(1, &mask, NULL);
08751    return 0;
08752 }

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

07180 {
07181    int res = 0;
07182 
07183    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
07184       res |= reload_queue_rules(reload);
07185    }
07186    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
07187       res |= clear_stats(queuename);
07188    }
07189    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
07190       res |= reload_queues(reload, mask, queuename);
07191    }
07192    return res;
07193 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 5611 of file app_queue.c.

References add_to_queue(), ao2_t_find, ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), ast_debug, ast_log(), ast_strlen_zero(), ERANGE, errno, find_load_queue_rt_friendly(), member::interface, ast_db_entry::key, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_t_unref, queues, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

05612 {
05613    char *cur_ptr;
05614    const char *queue_name;
05615    char *member;
05616    char *interface;
05617    char *membername = NULL;
05618    char *state_interface;
05619    char *penalty_tok;
05620    int penalty = 0;
05621    char *paused_tok;
05622    int paused = 0;
05623    struct ast_db_entry *db_tree;
05624    struct ast_db_entry *entry;
05625    struct call_queue *cur_queue;
05626    char queue_data[PM_MAX_LEN];
05627 
05628    /* Each key in 'pm_family' is the name of a queue */
05629    db_tree = ast_db_gettree(pm_family, NULL);
05630    for (entry = db_tree; entry; entry = entry->next) {
05631 
05632       queue_name = entry->key + strlen(pm_family) + 2;
05633 
05634       {
05635          struct call_queue tmpq = {
05636             .name = queue_name,
05637          };
05638          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05639       }
05640 
05641       if (!cur_queue) {
05642          cur_queue = find_load_queue_rt_friendly(queue_name);
05643       }
05644 
05645       if (!cur_queue) {
05646          /* If the queue no longer exists, remove it from the
05647           * database */
05648          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05649          ast_db_del(pm_family, queue_name);
05650          continue;
05651       }
05652 
05653       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
05654          queue_t_unref(cur_queue, "Expire reload reference");
05655          continue;
05656       }
05657 
05658       cur_ptr = queue_data;
05659       while ((member = strsep(&cur_ptr, ",|"))) {
05660          if (ast_strlen_zero(member))
05661             continue;
05662 
05663          interface = strsep(&member, ";");
05664          penalty_tok = strsep(&member, ";");
05665          paused_tok = strsep(&member, ";");
05666          membername = strsep(&member, ";");
05667          state_interface = strsep(&member, ";");
05668 
05669          if (!penalty_tok) {
05670             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05671             break;
05672          }
05673          penalty = strtol(penalty_tok, NULL, 10);
05674          if (errno == ERANGE) {
05675             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05676             break;
05677          }
05678          
05679          if (!paused_tok) {
05680             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05681             break;
05682          }
05683          paused = strtol(paused_tok, NULL, 10);
05684          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05685             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05686             break;
05687          }
05688 
05689          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05690          
05691          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05692             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05693             break;
05694          }
05695       }
05696       queue_t_unref(cur_queue, "Expire reload reference");
05697    }
05698 
05699    if (db_tree) {
05700       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05701       ast_db_freetree(db_tree);
05702    }
05703 }

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 6769 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, rule_list::rules, and ast_variable::value.

Referenced by reload_handler().

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

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 7089 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_NOLOCK, OBJ_UNLINK, QUEUE_RELOAD_PARAMETERS, queue_set_global_params(), queues, and reload_single_queue().

Referenced by reload_handler().

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

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

References ao2_find, ao2_link, ao2_ref, 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, call_queue::members, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, and member::penalty.

Referenced by reload_single_queue().

06865 {
06866    char *membername, *interface, *state_interface, *tmp;
06867    char *parse;
06868    struct member *cur, *newm;
06869    struct member tmpmem;
06870    int penalty;
06871    AST_DECLARE_APP_ARGS(args,
06872       AST_APP_ARG(interface);
06873       AST_APP_ARG(penalty);
06874       AST_APP_ARG(membername);
06875       AST_APP_ARG(state_interface);
06876    );
06877 
06878    if (ast_strlen_zero(memberdata)) {
06879       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06880       return;
06881    }
06882 
06883    /* Add a new member */
06884    parse = ast_strdupa(memberdata);
06885             
06886    AST_STANDARD_APP_ARGS(args, parse);
06887 
06888    interface = args.interface;
06889    if (!ast_strlen_zero(args.penalty)) {
06890       tmp = args.penalty;
06891       ast_strip(tmp);
06892       penalty = atoi(tmp);
06893       if (penalty < 0) {
06894          penalty = 0;
06895       }
06896    } else {
06897       penalty = 0;
06898    }
06899 
06900    if (!ast_strlen_zero(args.membername)) {
06901       membername = args.membername;
06902       ast_strip(membername);
06903    } else {
06904       membername = interface;
06905    }
06906 
06907    if (!ast_strlen_zero(args.state_interface)) {
06908       state_interface = args.state_interface;
06909       ast_strip(state_interface);
06910    } else {
06911       state_interface = interface;
06912    }
06913 
06914    /* Find the old position in the list */
06915    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06916    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
06917    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06918       ao2_link(q->members, newm);
06919       ao2_ref(newm, -1);
06920    }
06921    newm = NULL;
06922 
06923    if (cur) {
06924       ao2_ref(cur, -1);
06925    }
06926 }

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 6960 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, call_queue::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_POINTER, OBJ_UNLINK, 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().

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

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

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

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

05300 {
05301    struct call_queue *q, tmpq = {
05302       .name = queuename,
05303    };
05304    struct member *mem, tmpmem;
05305    int res = RES_NOSUCHQUEUE;
05306 
05307    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05308    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05309       ao2_lock(q);
05310       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05311          /* XXX future changes should beware of this assumption!! */
05312          /*Change Penalty on realtime users*/
05313          if (mem->realtime && !ast_strlen_zero(mem->rt_uniqueid) && negative_penalty_invalid) {
05314             update_realtime_member_field(mem, q->name, "penalty", "-1");
05315          } else if (!mem->dynamic) {
05316             ao2_ref(mem, -1);
05317             ao2_unlock(q);
05318             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05319             return RES_NOT_DYNAMIC;
05320          }
05321          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05322             "Queue: %s\r\n"
05323             "Location: %s\r\n"
05324             "MemberName: %s\r\n",
05325             q->name, mem->interface, mem->membername);
05326          ao2_unlink(q->members, mem);
05327          ao2_ref(mem, -1);
05328 
05329          if (queue_persistent_members)
05330             dump_queue_members(q);
05331          
05332          res = RES_OKAY;
05333       } else {
05334          res = RES_EXISTS;
05335       }
05336       ao2_unlock(q);
05337       queue_t_unref(q, "Expiring temporary reference");
05338    }
05339 
05340    return res;
05341 }

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 3087 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_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_name(), ast_channel_set_caller_event(), ast_channel_uniqueid(), ast_channel_unlock, ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_devstate2str(), ast_devstate_changed_literal(), AST_FLAG_ANSWERED_ELSEWHERE, ast_log(), ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_set_flag, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_cdr::clid, compare_weight(), ast_channel::connected, ast_channel::context, ast_channel::data, ast_cdr::dcontext, callattempt::dial_callerid_absent, ast_channel::dialed, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, get_queue_member_status(), ast_party_connected_line::id, ast_party_caller::id, member::ignorebusy, member::interface, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, LOG_WARNING, ast_channel::macroexten, manager_event, callattempt::member, member::membername, ast_party_id::name, call_queue::name, ast_channel::nativeformats, ast_party_dialed::number, ast_party_id::number, queue_ent::parent, member::paused, 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, member::status, status, callattempt::stillgoing, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_party_dialed::transit_network_select, update_status(), ast_cdr::userfield, ast_party_name::valid, ast_party_number::valid, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

03088 {
03089    int res;
03090    int status;
03091    char tech[256];
03092    char *location;
03093    const char *macrocontext, *macroexten;
03094    enum ast_device_state newstate;
03095 
03096    /* on entry here, we know that tmp->chan == NULL */
03097    if (tmp->member->paused) {
03098       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
03099       if (qe->chan->cdr) {
03100          ast_cdr_busy(qe->chan->cdr);
03101       }
03102       tmp->stillgoing = 0;
03103       return 0;
03104    }
03105 
03106    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
03107       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
03108       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n",
03109             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
03110       if (qe->chan->cdr) {
03111          ast_cdr_busy(qe->chan->cdr);
03112       }
03113       tmp->stillgoing = 0;
03114       (*busies)++;
03115       return 0;
03116    }
03117 
03118    if (!qe->parent->ringinuse || !tmp->member->ignorebusy) {
03119       if (check_state_unknown && (tmp->member->status == AST_DEVICE_UNKNOWN)) {
03120          newstate = ast_device_state(tmp->member->interface);
03121          if (newstate != tmp->member->status) {
03122             ast_log(LOG_WARNING, "Found a channel matching iterface %s while status was %s changed to %s\n",
03123                tmp->member->interface, ast_devstate2str(tmp->member->status), ast_devstate2str(newstate));
03124             ast_devstate_changed_literal(newstate, tmp->member->interface);
03125          }
03126       }
03127       if ((tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
03128          ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
03129          if (qe->chan->cdr) {
03130             ast_cdr_busy(qe->chan->cdr);
03131          }
03132          tmp->stillgoing = 0;
03133          return 0;
03134       }
03135    }
03136 
03137    if (use_weight && compare_weight(qe->parent,tmp->member)) {
03138       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
03139       if (qe->chan->cdr) {
03140          ast_cdr_busy(qe->chan->cdr);
03141       }
03142       tmp->stillgoing = 0;
03143       (*busies)++;
03144       return 0;
03145    }
03146 
03147    ast_copy_string(tech, tmp->interface, sizeof(tech));
03148    if ((location = strchr(tech, '/')))
03149       *location++ = '\0';
03150    else
03151       location = "";
03152 
03153    /* Request the peer */
03154    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03155    if (!tmp->chan) {       /* If we can't, just go on to the next call */
03156       if (qe->chan->cdr) {
03157          ast_cdr_busy(qe->chan->cdr);
03158       }
03159       tmp->stillgoing = 0;
03160 
03161       ao2_lock(qe->parent);
03162       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03163       qe->parent->rrpos++;
03164       qe->linpos++;
03165       ao2_unlock(qe->parent);
03166 
03167       (*busies)++;
03168       return 0;
03169    }
03170 
03171    ast_channel_lock_both(tmp->chan, qe->chan);
03172 
03173    if (qe->cancel_answered_elsewhere) {
03174       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03175    }
03176    tmp->chan->appl = "AppQueue";
03177    tmp->chan->data = "(Outgoing Line)";
03178    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03179 
03180    /* If the new channel has no callerid, try to guess what it should be */
03181    if (!tmp->chan->caller.id.number.valid) {
03182       if (qe->chan->connected.id.number.valid) {
03183          struct ast_party_caller caller;
03184 
03185          ast_party_caller_set_init(&caller, &tmp->chan->caller);
03186          caller.id = qe->chan->connected.id;
03187          caller.ani = qe->chan->connected.ani;
03188          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03189       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03190          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03191       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03192          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
03193       }
03194       tmp->dial_callerid_absent = 1;
03195    }
03196 
03197    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03198 
03199    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03200 
03201    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03202 
03203    /* Inherit specially named variables from parent channel */
03204    ast_channel_inherit_variables(qe->chan, tmp->chan);
03205    ast_channel_datastore_inherit(qe->chan, tmp->chan);
03206 
03207    /* Presense of ADSI CPE on outgoing channel follows ours */
03208    tmp->chan->adsicpe = qe->chan->adsicpe;
03209 
03210    /* Inherit context and extension */
03211    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03212    ast_channel_dialcontext_set(tmp->chan, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03213    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03214    if (!ast_strlen_zero(macroexten))
03215       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03216    else
03217       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03218    if (ast_cdr_isset_unanswered()) {
03219       /* they want to see the unanswered dial attempts! */
03220       /* set up the CDR fields on all the CDRs to give sensical information */
03221       ast_cdr_setdestchan(tmp->chan->cdr, ast_channel_name(tmp->chan));
03222       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03223       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03224       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03225       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03226       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03227       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03228       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03229       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03230       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03231       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03232    }
03233 
03234    ast_channel_unlock(tmp->chan);
03235    ast_channel_unlock(qe->chan);
03236 
03237    /* Place the call, but don't wait on the answer */
03238    if ((res = ast_call(tmp->chan, location, 0))) {
03239       /* Again, keep going even if there's an error */
03240       ast_debug(1, "ast call on peer returned %d\n", res);
03241       ast_verb(3, "Couldn't call %s\n", tmp->interface);
03242       do_hang(tmp);
03243       (*busies)++;
03244       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03245       return 0;
03246    } else if (qe->parent->eventwhencalled) {
03247       char vars[2048];
03248 
03249       ast_channel_lock_both(tmp->chan, qe->chan);
03250 
03251       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03252          "Queue: %s\r\n"
03253          "AgentCalled: %s\r\n"
03254          "AgentName: %s\r\n"
03255          "ChannelCalling: %s\r\n"
03256          "DestinationChannel: %s\r\n"
03257          "CallerIDNum: %s\r\n"
03258          "CallerIDName: %s\r\n"
03259          "ConnectedLineNum: %s\r\n"
03260          "ConnectedLineName: %s\r\n"
03261          "Context: %s\r\n"
03262          "Extension: %s\r\n"
03263          "Priority: %d\r\n"
03264          "Uniqueid: %s\r\n"
03265          "%s",
03266          qe->parent->name, tmp->interface, tmp->member->membername, ast_channel_name(qe->chan), ast_channel_name(tmp->chan),
03267          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
03268          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
03269          S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),
03270          S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"),
03271          qe->chan->context, qe->chan->exten, qe->chan->priority, ast_channel_uniqueid(qe->chan),
03272          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03273 
03274       ast_channel_unlock(tmp->chan);
03275       ast_channel_unlock(qe->chan);
03276 
03277       ast_verb(3, "Called %s\n", tmp->interface);
03278    }
03279 
03280    update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03281    return 1;
03282 }

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

03311 {
03312    int ret = 0;
03313 
03314    while (ret == 0) {
03315       struct callattempt *best = find_best(outgoing);
03316       if (!best) {
03317          ast_debug(1, "Nobody left to try ringing in queue\n");
03318          break;
03319       }
03320       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03321          struct callattempt *cur;
03322          /* Ring everyone who shares this best metric (for ringall) */
03323          for (cur = outgoing; cur; cur = cur->q_next) {
03324             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03325                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03326                ret |= ring_entry(qe, cur, busies);
03327             }
03328          }
03329       } else {
03330          /* Ring just the best channel */
03331          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03332          ret = ring_entry(qe, best, busies);
03333       }
03334       
03335       /* If we have timed out, break out */
03336       if (qe->expire && (time(NULL) >= qe->expire)) {
03337          ast_debug(1, "Queue timed out while ringing members.\n");
03338          ret = 0;
03339          break;
03340       }
03341    }
03342 
03343    return ret;
03344 }

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

References ao2_lock, ao2_ref, ao2_unlock, ast_channel_name(), ast_channel_uniqueid(), ast_indicate(), ast_moh_start(), ast_queue_log(), ast_verb, call_queue::autopause, call_queue::autopausedelay, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, interface_exists(), member::lastcall, manager_event, queue_ent::moh, call_queue::name, 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().

03469 {
03470    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03471 
03472    /* Stop ringing, and resume MOH if specified */
03473    if (qe->ring_when_ringing) {
03474       ast_indicate(qe->chan, -1);
03475       ast_moh_start(qe->chan, qe->moh, NULL);
03476    }
03477 
03478    if (qe->parent->eventwhencalled) {
03479       char vars[2048];
03480 
03481       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03482                   "Queue: %s\r\n"
03483                   "Uniqueid: %s\r\n"
03484                   "Channel: %s\r\n"
03485                   "Member: %s\r\n"
03486                   "MemberName: %s\r\n"
03487                   "Ringtime: %d\r\n"
03488                   "%s",
03489                   qe->parent->name,
03490                   ast_channel_uniqueid(qe->chan),
03491                   ast_channel_name(qe->chan),
03492                   interface,
03493                   membername,
03494                   rnatime,
03495                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03496    }
03497    ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), membername, "RINGNOANSWER", "%d", rnatime);
03498    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03499       if (qe->parent->autopausedelay > 0) {
03500          struct member *mem;
03501          ao2_lock(qe->parent);
03502          if ((mem = interface_exists(qe->parent, interface))) {
03503             time_t idletime = time(&idletime)-mem->lastcall;
03504             if ((mem->lastcall != 0) && (qe->parent->autopausedelay > idletime)) {
03505                ao2_unlock(qe->parent);
03506                ao2_ref(mem, -1);
03507                return;
03508             }
03509             ao2_ref(mem, -1);
03510          }
03511          ao2_unlock(qe->parent);
03512       }
03513       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03514          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03515             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03516                interface, qe->parent->name);
03517          } else {
03518             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03519          }
03520       } else {
03521          /* If queue autopause is mode all, just don't send any queue to stop.
03522          * the function will stop in all queues */
03523          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03524             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03525                   interface, qe->parent->name);
03526          } else {
03527                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03528          }
03529       }
03530    }
03531    return;
03532 }

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

RemoveQueueMember application.

Definition at line 5778 of file app_queue.c.

References ao2_ref, args, AST_APP_ARG, ast_channel_name(), ast_channel_uniqueid(), ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), find_member_by_queuename_and_interface(), member::interface, LOG_NOTICE, LOG_WARNING, member::membername, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

05779 {
05780    int res=-1;
05781    char *parse, *temppos = NULL;
05782    struct member *mem = NULL;
05783 
05784    AST_DECLARE_APP_ARGS(args,
05785       AST_APP_ARG(queuename);
05786       AST_APP_ARG(interface);
05787       AST_APP_ARG(options);
05788    );
05789 
05790 
05791    if (ast_strlen_zero(data)) {
05792       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
05793       return -1;
05794    }
05795 
05796    parse = ast_strdupa(data);
05797 
05798    AST_STANDARD_APP_ARGS(args, parse);
05799 
05800    if (ast_strlen_zero(args.interface)) {
05801       args.interface = ast_strdupa(ast_channel_name(chan));
05802       temppos = strrchr(args.interface, '-');
05803       if (temppos)
05804          *temppos = '\0';
05805    }
05806 
05807    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05808 
05809    if (log_membername_as_agent) {
05810       mem = find_member_by_queuename_and_interface(args.queuename, args.interface);
05811    }
05812 
05813    switch (remove_from_queue(args.queuename, args.interface)) {
05814    case RES_OKAY:
05815       if (!mem || ast_strlen_zero(mem->membername)) {
05816          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), args.interface, "REMOVEMEMBER", "%s", "");
05817       } else {
05818          ast_queue_log(args.queuename, ast_channel_uniqueid(chan), mem->membername, "REMOVEMEMBER", "%s", "");
05819       }
05820       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05821       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05822       res = 0;
05823       break;
05824    case RES_EXISTS:
05825       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05826       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05827       res = 0;
05828       break;
05829    case RES_NOSUCHQUEUE:
05830       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05831       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05832       res = 0;
05833       break;
05834    case RES_NOT_DYNAMIC:
05835       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05836       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05837       res = 0;
05838       break;
05839    }
05840 
05841    if (mem) {
05842       ao2_ref(mem, -1);
05843    }
05844 
05845    return res;
05846 }

static void rt_handle_member_record ( struct call_queue q,
char *  interface,
struct ast_config member_config 
) [static]

Find rt member record to update otherwise create one.

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

Definition at line 2127 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_log(), ast_queue_log(), ast_strlen_zero(), ast_true(), ast_variable_retrieve(), create_queue_member(), member::dead, member::ignorebusy, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, 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().

02128 {
02129    struct member *m;
02130    struct ao2_iterator mem_iter;
02131    int penalty = 0;
02132    int paused  = 0;
02133    int found = 0;
02134    int ignorebusy = 0;
02135 
02136    const char *config_val;
02137    const char *rt_uniqueid = ast_variable_retrieve(member_config, interface, "uniqueid");
02138    const char *membername = S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface);
02139    const char *state_interface = S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface);
02140    const char *penalty_str = ast_variable_retrieve(member_config, interface, "penalty");
02141    const char *paused_str = ast_variable_retrieve(member_config, interface, "paused");
02142 
02143    if (ast_strlen_zero(rt_uniqueid)) {
02144       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02145       return;
02146    }
02147 
02148    if (penalty_str) {
02149       penalty = atoi(penalty_str);
02150       if ((penalty < 0) && negative_penalty_invalid) {
02151          return;
02152       } else if (penalty < 0) {
02153          penalty = 0;
02154       }
02155    }
02156 
02157    if (paused_str) {
02158       paused = atoi(paused_str);
02159       if (paused < 0) {
02160          paused = 0;
02161       }
02162    }
02163 
02164    if ((config_val = ast_variable_retrieve(member_config, interface, "ignorebusy"))) {
02165       ignorebusy = ast_true(config_val);
02166    } else {
02167       ignorebusy = 1;
02168    }
02169 
02170    /* Find member by realtime uniqueid and update */
02171    mem_iter = ao2_iterator_init(q->members, 0);
02172    while ((m = ao2_iterator_next(&mem_iter))) {
02173       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02174          m->dead = 0;   /* Do not delete this one. */
02175          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02176          if (paused_str) {
02177             m->paused = paused;
02178          }
02179          if (strcasecmp(state_interface, m->state_interface)) {
02180             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02181          }
02182          m->penalty = penalty;
02183          m->ignorebusy = ignorebusy;
02184          found = 1;
02185          ao2_ref(m, -1);
02186          break;
02187       }
02188       ao2_ref(m, -1);
02189    }
02190    ao2_iterator_destroy(&mem_iter);
02191 
02192    /* Create a new member */
02193    if (!found) {
02194       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02195          m->dead = 0;
02196          m->realtime = 1;
02197          m->ignorebusy = ignorebusy;
02198          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02199          if (!log_membername_as_agent) {
02200             ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02201          } else {
02202             ast_queue_log(q->name, "REALTIME", m->membername, "ADDMEMBER", "%s", paused ? "PAUSED" : "");
02203          }
02204          ao2_link(q->members, m);
02205          ao2_ref(m, -1);
02206          m = NULL;
02207       }
02208    }
02209 }

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

Playback announcement to queued members if period has elapsed.

Definition at line 3395 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

03396 {
03397    int res = 0;
03398    time_t now;
03399 
03400    /* Get the current time */
03401    time(&now);
03402 
03403    /* Check to see if it is time to announce */
03404    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03405       return 0;
03406 
03407    /* Stop the music on hold so we can play our own file */
03408    if (ringing)
03409       ast_indicate(qe->chan,-1);
03410    else
03411       ast_moh_stop(qe->chan);
03412 
03413    ast_verb(3, "Playing periodic announcement\n");
03414    
03415    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03416       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03417    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03418       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03419       qe->last_periodic_announce_sound = 0;
03420    }
03421    
03422    /* play the announcement */
03423    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03424 
03425    if (res > 0 && !valid_exit(qe, res))
03426       res = 0;
03427 
03428    /* Resume Music on Hold if the caller is going to stay in the queue */
03429    if (!res) {
03430       if (ringing)
03431          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03432       else
03433          ast_moh_start(qe->chan, qe->moh, NULL);
03434    }
03435 
03436    /* update last_periodic_announce_time */
03437    if (qe->parent->relativeperiodicannounce)
03438       time(&qe->last_periodic_announce_time);
03439    else
03440       qe->last_periodic_announce_time = now;
03441 
03442    /* Update the current periodic announcement to the next announcement */
03443    if (!qe->parent->randomperiodicannounce) {
03444       qe->last_periodic_announce_sound++;
03445    }
03446    
03447    return res;
03448 }

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

Definition at line 2673 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_channel_language(), ast_channel_name(), 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, call_queue::name, queue_ent::parent, play_file(), queue_ent::pos, call_queue::queue_quantity1, call_queue::queue_quantity2, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

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

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

References AGENT, ast_channel_name(), ast_channel_uniqueid(), 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().

04243 {
04244    const char *reason = NULL; /* silence dumb compilers */
04245 
04246    if (!qe->parent->eventwhencalled)
04247       return;
04248 
04249    switch (rsn) {
04250    case CALLER:
04251       reason = "caller";
04252       break;
04253    case AGENT:
04254       reason = "agent";
04255       break;
04256    case TRANSFER:
04257       reason = "transfer";
04258       break;
04259    }
04260 
04261    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04262       "Queue: %s\r\n"
04263       "Uniqueid: %s\r\n"
04264       "Channel: %s\r\n"
04265       "Member: %s\r\n"
04266       "MemberName: %s\r\n"
04267       "HoldTime: %ld\r\n"
04268       "TalkTime: %ld\r\n"
04269       "Reason: %s\r\n"
04270       "%s",
04271       queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
04272       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04273       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04274 }

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

Definition at line 5404 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, call_queue::name, 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().

05405 {
05406    int found = 0;
05407    struct call_queue *q;
05408    struct member *mem;
05409    struct ao2_iterator queue_iter;
05410    int failed;
05411 
05412    /* Special event for when all queues are paused - individual events still generated */
05413    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05414    if (ast_strlen_zero(queuename))
05415       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05416 
05417    queue_iter = ao2_iterator_init(queues, 0);
05418    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05419       ao2_lock(q);
05420       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05421          if ((mem = interface_exists(q, interface))) {
05422             if (mem->paused == paused) {
05423                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05424             }
05425 
05426             failed = 0;
05427             if (mem->realtime) {
05428                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05429             }
05430 
05431             if (failed) {
05432                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05433                ao2_ref(mem, -1);
05434                ao2_unlock(q);
05435                queue_t_unref(q, "Done with iterator");
05436                continue;
05437             }
05438             found++;
05439             mem->paused = paused;
05440 
05441             if (queue_persistent_members)
05442                dump_queue_members(q);
05443 
05444             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05445 
05446             if (!ast_strlen_zero(reason)) {
05447                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05448                   "Queue: %s\r\n"
05449                   "Location: %s\r\n"
05450                   "MemberName: %s\r\n"
05451                   "Paused: %d\r\n"
05452                   "Reason: %s\r\n",
05453                      q->name, mem->interface, mem->membername, paused, reason);
05454             } else {
05455                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05456                   "Queue: %s\r\n"
05457                   "Location: %s\r\n"
05458                   "MemberName: %s\r\n"
05459                   "Paused: %d\r\n",
05460                      q->name, mem->interface, mem->membername, paused);
05461             }
05462             ao2_ref(mem, -1);
05463          }
05464       }
05465 
05466       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05467          ao2_unlock(q);
05468          queue_t_unref(q, "Done with iterator");
05469          break;
05470       }
05471 
05472       ao2_unlock(q);
05473       queue_t_unref(q, "Done with iterator");
05474    }
05475    ao2_iterator_destroy(&queue_iter);
05476 
05477    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05478 }

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

Definition at line 5524 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_category_browse(), ast_check_realtime(), ast_load_realtime_multientry(), ast_log(), ast_strlen_zero(), find_load_queue_rt_friendly(), LOG_ERROR, queues, RESULT_FAILURE, RESULT_SUCCESS, SENTINEL, and set_member_penalty_help_members().

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

05525 {
05526    int foundinterface = 0, foundqueue = 0;
05527    struct call_queue *q;
05528    struct ast_config *queue_config = NULL;
05529    struct ao2_iterator queue_iter;
05530 
05531    if (penalty < 0 && !negative_penalty_invalid) {
05532       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05533       return RESULT_FAILURE;
05534    }
05535 
05536    if (ast_strlen_zero(queuename)) { /* This means we need to iterate through all the queues. */
05537       if (ast_check_realtime("queues")) {
05538          char *queuename;
05539          queue_config = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05540          if (queue_config) {
05541             for (queuename = ast_category_browse(queue_config, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(queue_config, queuename)) {
05542                if ((q = find_load_queue_rt_friendly(queuename))) {
05543                   foundqueue++;
05544                   foundinterface += set_member_penalty_help_members(q, interface, penalty);
05545                }
05546             }
05547          }
05548       }
05549 
05550       /* After hitting realtime queues, go back and get the regular ones. */
05551       queue_iter = ao2_iterator_init(queues, 0);
05552       while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05553          foundqueue++;
05554          foundinterface += set_member_penalty_help_members(q, interface, penalty);
05555       }
05556       ao2_iterator_destroy(&queue_iter);
05557    } else { /* We actually have a queuename, so we can just act on the single queue. */
05558       if ((q = find_load_queue_rt_friendly(queuename))) {
05559          foundqueue++;
05560          foundinterface += set_member_penalty_help_members(q, interface, penalty);
05561       }
05562    }
05563 
05564    if (foundinterface) {
05565       return RESULT_SUCCESS;
05566    } else if (!foundqueue) {
05567       ast_log (LOG_ERROR, "Invalid queuename\n");
05568    } else {
05569       ast_log (LOG_ERROR, "Invalid interface\n");
05570    }
05571 
05572    return RESULT_FAILURE;
05573 }

static int set_member_penalty_help_members ( struct call_queue q,
const char *  interface,
int  penalty 
) [static]

Definition at line 5489 of file app_queue.c.

References ao2_lock, ao2_ref, ao2_unlock, ast_queue_log(), EVENT_FLAG_AGENT, member::interface, interface_exists(), manager_event, call_queue::name, member::penalty, member::realtime, and update_realtime_member_field().

Referenced by set_member_penalty().

05490 {
05491    struct member *mem;
05492    int foundinterface = 0;
05493    char rtpenalty[80];
05494 
05495    ao2_lock(q);
05496    if ((mem = interface_exists(q, interface))) {
05497       foundinterface++;
05498       if (!mem->realtime) {
05499          mem->penalty = penalty;
05500       } else {
05501          sprintf(rtpenalty, "%i", penalty);
05502          update_realtime_member_field(mem, q->name, "penalty", rtpenalty);
05503       }
05504       ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05505       manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05506          "Queue: %s\r\n"
05507          "Location: %s\r\n"
05508          "Penalty: %d\r\n",
05509          q->name, mem->interface, penalty);
05510       ao2_ref(mem, -1);
05511    }
05512    ao2_unlock(q);
05513 
05514    return foundinterface;
05515 }

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

sets the QUEUESTATUS channel variable

Definition at line 1224 of file app_queue.c.

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

Referenced by queue_exec().

01225 {
01226    int i;
01227 
01228    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
01229       if (queue_results[i].id == res) {
01230          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
01231          return;
01232       }
01233    }
01234 }

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

Set variables of queue.

Definition at line 1319 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, call_queue::name, 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().

01320 {
01321    char interfacevar[256]="";
01322    float sl = 0;
01323 
01324    ao2_lock(q);
01325 
01326    if (q->setqueuevar) {
01327       sl = 0;
01328       if (q->callscompleted > 0) 
01329          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01330 
01331       snprintf(interfacevar, sizeof(interfacevar),
01332          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01333          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01334 
01335       ao2_unlock(q);
01336    
01337       pbx_builtin_setvar_multiple(chan, interfacevar); 
01338    } else {
01339       ao2_unlock(q);
01340    }
01341 }

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

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

Referenced by try_calling().

04345 {
04346    struct ast_datastore *ds;
04347    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04348 
04349    if (!qtds) {
04350       ast_log(LOG_WARNING, "Memory allocation error!\n");
04351       return NULL;
04352    }
04353 
04354    ast_channel_lock(qe->chan);
04355    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04356       ast_channel_unlock(qe->chan);
04357       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04358       return NULL;
04359    }
04360 
04361    qtds->qe = qe;
04362    /* This member is refcounted in try_calling, so no need to add it here, too */
04363    qtds->member = member;
04364    qtds->starttime = starttime;
04365    qtds->callcompletedinsl = callcompletedinsl;
04366    ds->data = qtds;
04367    ast_channel_datastore_add(qe->chan, ds);
04368    ast_channel_unlock(qe->chan);
04369    return ds;
04370 }

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

03372 {
03373    struct callattempt *best = find_best(outgoing);
03374 
03375    if (best) {
03376       /* Ring just the best channel */
03377       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03378       qe->linpos = best->metric % 1000;
03379    } else {
03380       /* Just increment rrpos */
03381       if (qe->linwrapped) {
03382          /* No more channels, start over */
03383          qe->linpos = 0;
03384       } else {
03385          /* Prioritize next entry */
03386          qe->linpos++;
03387       }
03388    }
03389    qe->linwrapped = 0;
03390 
03391    return 0;
03392 }

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

03348 {
03349    struct callattempt *best = find_best(outgoing);
03350 
03351    if (best) {
03352       /* Ring just the best channel */
03353       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03354       qe->parent->rrpos = best->metric % 1000;
03355    } else {
03356       /* Just increment rrpos */
03357       if (qe->parent->wrapped) {
03358          /* No more channels, start over */
03359          qe->parent->rrpos = 0;
03360       } else {
03361          /* Prioritize next entry */
03362          qe->parent->rrpos++;
03363       }
03364    }
03365    qe->parent->wrapped = 0;
03366 
03367    return 0;
03368 }

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

Definition at line 1248 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

01249 {
01250    int x;
01251 
01252    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01253       if (!strcasecmp(strategy, strategies[x].name))
01254          return strategies[x].strategy;
01255    }
01256 
01257    return -1;
01258 }

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 4424 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, 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_language(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_name(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_uniqueid(), 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(), 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, errno, 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, call_queue::membergosub, call_queue::membermacro, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, call_queue::name, 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, queue_transfer_info, 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(), call_queue::sound_callerannounce, call_queue::sound_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, 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_connectedline(), update_queue(), ast_party_number::valid, vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.

Referenced by queue_exec().

04425 {
04426    struct member *cur;
04427    struct callattempt *outgoing = NULL; /* the list of calls we are building */
04428    int to, orig;
04429    char oldexten[AST_MAX_EXTENSION]="";
04430    char oldcontext[AST_MAX_CONTEXT]="";
04431    char queuename[256]="";
04432    char interfacevar[256]="";
04433    struct ast_channel *peer;
04434    struct ast_channel *which;
04435    struct callattempt *lpeer;
04436    struct member *member;
04437    struct ast_app *application;
04438    int res = 0, bridge = 0;
04439    int numbusies = 0;
04440    int x=0;
04441    char *announce = NULL;
04442    char digit = 0;
04443    time_t callstart;
04444    time_t now = time(NULL);
04445    struct ast_bridge_config bridge_config;
04446    char nondataquality = 1;
04447    char *agiexec = NULL;
04448    char *macroexec = NULL;
04449    char *gosubexec = NULL;
04450    const char *monitorfilename;
04451    const char *monitor_exec;
04452    const char *monitor_options;
04453    char tmpid[256], tmpid2[256];
04454    char meid[1024], meid2[1024];
04455    char mixmonargs[1512];
04456    struct ast_app *mixmonapp = NULL;
04457    char *p;
04458    char vars[2048];
04459    int forwardsallowed = 1;
04460    int update_connectedline = 1;
04461    int callcompletedinsl;
04462    struct ao2_iterator memi;
04463    struct ast_datastore *datastore, *transfer_ds;
04464    struct queue_end_bridge *queue_end_bridge = NULL;
04465 
04466    ast_channel_lock(qe->chan);
04467    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04468    ast_channel_unlock(qe->chan);
04469 
04470    memset(&bridge_config, 0, sizeof(bridge_config));
04471    tmpid[0] = 0;
04472    meid[0] = 0;
04473    time(&now);
04474 
04475    /* If we've already exceeded our timeout, then just stop
04476     * This should be extremely rare. queue_exec will take care
04477     * of removing the caller and reporting the timeout as the reason.
04478     */
04479    if (qe->expire && now >= qe->expire) {
04480       res = 0;
04481       goto out;
04482    }
04483       
04484    for (; options && *options; options++)
04485       switch (*options) {
04486       case 't':
04487          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04488          break;
04489       case 'T':
04490          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04491          break;
04492       case 'w':
04493          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04494          break;
04495       case 'W':
04496          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04497          break;
04498       case 'c':
04499          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04500          break;
04501       case 'd':
04502          nondataquality = 0;
04503          break;
04504       case 'h':
04505          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04506          break;
04507       case 'H':
04508          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04509          break;
04510       case 'k':
04511          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04512          break;
04513       case 'K':
04514          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04515          break;
04516       case 'n':
04517          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED)
04518             (*tries)++;
04519          else
04520             *tries = ao2_container_count(qe->parent->members);
04521          *noption = 1;
04522          break;
04523       case 'i':
04524          forwardsallowed = 0;
04525          break;
04526       case 'I':
04527          update_connectedline = 0;
04528          break;
04529       case 'x':
04530          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04531          break;
04532       case 'X':
04533          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04534          break;
04535       case 'C':
04536          qe->cancel_answered_elsewhere = 1;
04537          break;
04538       }
04539 
04540    /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 
04541       (this is mainly to support chan_local)
04542    */
04543    if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04544       qe->cancel_answered_elsewhere = 1;
04545    }
04546 
04547    ao2_lock(qe->parent);
04548    ast_debug(1, "%s is trying to call a queue member.\n",
04549                      ast_channel_name(qe->chan));
04550    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04551    if (!ast_strlen_zero(qe->announce))
04552       announce = qe->announce;
04553    if (!ast_strlen_zero(announceoverride))
04554       announce = announceoverride;
04555 
04556    memi = ao2_iterator_init(qe->parent->members, 0);
04557    while ((cur = ao2_iterator_next(&memi))) {
04558       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04559       struct ast_dialed_interface *di;
04560       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04561       if (!tmp) {
04562          ao2_ref(cur, -1);
04563          ao2_iterator_destroy(&memi);
04564          ao2_unlock(qe->parent);
04565          goto out;
04566       }
04567       if (!datastore) {
04568          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04569             callattempt_free(tmp);
04570             ao2_ref(cur, -1);
04571             ao2_iterator_destroy(&memi);
04572             ao2_unlock(qe->parent);
04573             goto out;
04574          }
04575          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04576          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04577             callattempt_free(tmp);
04578             ao2_ref(cur, -1);
04579             ao2_iterator_destroy(&memi);
04580             ao2_unlock(&qe->parent);
04581             goto out;
04582          }
04583          datastore->data = dialed_interfaces;
04584          AST_LIST_HEAD_INIT(dialed_interfaces);
04585 
04586          ast_channel_lock(qe->chan);
04587          ast_channel_datastore_add(qe->chan, datastore);
04588          ast_channel_unlock(qe->chan);
04589       } else
04590          dialed_interfaces = datastore->data;
04591 
04592       AST_LIST_LOCK(dialed_interfaces);
04593       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04594          if (!strcasecmp(cur->interface, di->interface)) {
04595             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
04596                di->interface);
04597             break;
04598          }
04599       }
04600       AST_LIST_UNLOCK(dialed_interfaces);
04601 
04602       if (di) {
04603          callattempt_free(tmp);
04604          ao2_ref(cur, -1);
04605          continue;
04606       }
04607 
04608       /* It is always ok to dial a Local interface.  We only keep track of
04609        * which "real" interfaces have been dialed.  The Local channel will
04610        * inherit this list so that if it ends up dialing a real interface,
04611        * it won't call one that has already been called. */
04612       if (strncasecmp(cur->interface, "Local/", 6)) {
04613          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
04614             callattempt_free(tmp);
04615             ao2_ref(cur, -1);
04616             ao2_iterator_destroy(&memi);
04617             ao2_unlock(qe->parent);
04618             goto out;
04619          }
04620          strcpy(di->interface, cur->interface);
04621 
04622          AST_LIST_LOCK(dialed_interfaces);
04623          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
04624          AST_LIST_UNLOCK(dialed_interfaces);
04625       }
04626 
04627       ast_channel_lock(qe->chan);
04628       /*
04629        * Seed the callattempt's connected line information with previously
04630        * acquired connected line info from the queued channel.  The
04631        * previously acquired connected line info could have been set
04632        * through the CONNECTED_LINE dialplan function.
04633        */
04634       ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
04635       ast_channel_unlock(qe->chan);
04636 
04637       tmp->stillgoing = -1;
04638       tmp->member = cur;/* Place the reference for cur into callattempt. */
04639       tmp->lastcall = cur->lastcall;
04640       tmp->lastqueue = cur->lastqueue;
04641       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
04642       /* Special case: If we ring everyone, go ahead and ring them, otherwise
04643          just calculate their metric for the appropriate strategy */
04644       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
04645          /* Put them in the list of outgoing thingies...  We're ready now.
04646             XXX If we're forcibly removed, these outgoing calls won't get
04647             hung up XXX */
04648          tmp->q_next = outgoing;
04649          outgoing = tmp;      
04650          /* If this line is up, don't try anybody else */
04651          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
04652             break;
04653       } else {
04654          callattempt_free(tmp);
04655       }
04656    }
04657    ao2_iterator_destroy(&memi);
04658 
04659    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
04660       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
04661       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
04662          to = (qe->expire - now) * 1000;
04663       else
04664          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
04665    } else {
04666       /* Config timeout is higher priority thatn application timeout */
04667       if (qe->expire && qe->expire<=now) {
04668          to = 0;
04669       } else if (qe->parent->timeout) {
04670          to = qe->parent->timeout * 1000;
04671       } else {
04672          to = -1;
04673       }
04674    }
04675    orig = to;
04676    ++qe->pending;
04677    ao2_unlock(qe->parent);
04678    ring_one(qe, outgoing, &numbusies);
04679    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline);
04680    /* The ast_channel_datastore_remove() function could fail here if the
04681     * datastore was moved to another channel during a masquerade. If this is
04682     * the case, don't free the datastore here because later, when the channel
04683     * to which the datastore was moved hangs up, it will attempt to free this
04684     * datastore again, causing a crash
04685     */
04686    ast_channel_lock(qe->chan);
04687    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
04688       ast_datastore_free(datastore);
04689    }
04690    ast_channel_unlock(qe->chan);
04691    ao2_lock(qe->parent);
04692    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
04693       store_next_rr(qe, outgoing);
04694 
04695    }
04696    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
04697       store_next_lin(qe, outgoing);
04698    }
04699    ao2_unlock(qe->parent);
04700    peer = lpeer ? lpeer->chan : NULL;
04701    if (!peer) {
04702       qe->pending = 0;
04703       if (to) {
04704          /* Must gotten hung up */
04705          res = -1;
04706       } else {
04707          /* User exited by pressing a digit */
04708          res = digit;
04709       }
04710       if (res == -1)
04711          ast_debug(1, "%s: Nobody answered.\n", ast_channel_name(qe->chan));
04712       if (ast_cdr_isset_unanswered()) {
04713          /* channel contains the name of one of the outgoing channels
04714             in its CDR; zero out this CDR to avoid a dual-posting */
04715          struct callattempt *o;
04716          for (o = outgoing; o; o = o->q_next) {
04717             if (!o->chan) {
04718                continue;
04719             }
04720             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
04721                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
04722                break;
04723             }
04724          }
04725       }
04726    } else { /* peer is valid */
04727       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
04728          we will always return with -1 so that it is hung up properly after the
04729          conversation.  */
04730       if (!strcmp(qe->chan->tech->type, "DAHDI"))
04731          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04732       if (!strcmp(peer->tech->type, "DAHDI"))
04733          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04734       /* Update parameters for the queue */
04735       time(&now);
04736       recalc_holdtime(qe, (now - qe->start));
04737       ao2_lock(qe->parent);
04738       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
04739       ao2_unlock(qe->parent);
04740       member = lpeer->member;
04741       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
04742       ao2_ref(member, 1);
04743       hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
04744       outgoing = NULL;
04745       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
04746          int res2;
04747 
04748          res2 = ast_autoservice_start(qe->chan);
04749          if (!res2) {
04750             if (qe->parent->memberdelay) {
04751                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
04752                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
04753             }
04754             if (!res2 && announce) {
04755                play_file(peer, announce);
04756             }
04757             if (!res2 && qe->parent->reportholdtime) {
04758                if (!play_file(peer, qe->parent->sound_reporthold)) {
04759                   int holdtime, holdtimesecs;
04760 
04761                   time(&now);
04762                   holdtime = abs((now - qe->start) / 60);
04763                   holdtimesecs = abs((now - qe->start) % 60);
04764                   if (holdtime > 0) {
04765                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, ast_channel_language(peer), NULL);
04766                      play_file(peer, qe->parent->sound_minutes);
04767                   }
04768                   if (holdtimesecs > 1) {
04769                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, ast_channel_language(peer), NULL);
04770                      play_file(peer, qe->parent->sound_seconds);
04771                   }
04772                }
04773             }
04774          }
04775          res2 |= ast_autoservice_stop(qe->chan);
04776          if (ast_check_hangup(peer)) {
04777             /* Agent must have hung up */
04778             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", ast_channel_name(peer));
04779             ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "AGENTDUMP", "%s", "");
04780             if (qe->parent->eventwhencalled)
04781                manager_event(EVENT_FLAG_AGENT, "AgentDump",
04782                      "Queue: %s\r\n"
04783                      "Uniqueid: %s\r\n"
04784                      "Channel: %s\r\n"
04785                      "Member: %s\r\n"
04786                      "MemberName: %s\r\n"
04787                      "%s",
04788                      queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
04789                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04790             ast_hangup(peer);
04791             ao2_ref(member, -1);
04792             goto out;
04793          } else if (res2) {
04794             /* Caller must have hung up just before being connected*/
04795             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", ast_channel_name(peer));
04796             ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04797             record_abandoned(qe);
04798             ast_hangup(peer);
04799             ao2_ref(member, -1);
04800             return -1;
04801          }
04802       }
04803       /* Stop music on hold */
04804       if (ringing)
04805          ast_indicate(qe->chan,-1);
04806       else
04807          ast_moh_stop(qe->chan);
04808       /* If appropriate, log that we have a destination channel */
04809       if (qe->chan->cdr) {
04810          ast_cdr_setdestchan(qe->chan->cdr, ast_channel_name(peer));
04811       }
04812       /* Make sure channels are compatible */
04813       res = ast_channel_make_compatible(qe->chan, peer);
04814       if (res < 0) {
04815          ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "SYSCOMPAT", "%s", "");
04816          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", ast_channel_name(qe->chan), ast_channel_name(peer));
04817          record_abandoned(qe);
04818          ast_cdr_failed(qe->chan->cdr);
04819          ast_hangup(peer);
04820          ao2_ref(member, -1);
04821          return -1;
04822       }
04823 
04824       /* Play announcement to the caller telling it's his turn if defined */
04825       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
04826          if (play_file(qe->chan, qe->parent->sound_callerannounce))
04827             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
04828       }
04829 
04830       ao2_lock(qe->parent);
04831       /* if setinterfacevar is defined, make member variables available to the channel */
04832       /* use  pbx_builtin_setvar to set a load of variables with one call */
04833       if (qe->parent->setinterfacevar) {
04834          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
04835             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
04836          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04837          pbx_builtin_setvar_multiple(peer, interfacevar);
04838       }
04839       
04840       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
04841       /* use  pbx_builtin_setvar to set a load of variables with one call */
04842       if (qe->parent->setqueueentryvar) {
04843          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
04844             (long) time(NULL) - qe->start, qe->opos);
04845          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04846          pbx_builtin_setvar_multiple(peer, interfacevar);
04847       }
04848    
04849       ao2_unlock(qe->parent);
04850 
04851       /* try to set queue variables if configured to do so*/
04852       set_queue_variables(qe->parent, qe->chan);
04853       set_queue_variables(qe->parent, peer);
04854       
04855       ast_channel_lock(qe->chan);
04856       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
04857             monitorfilename = ast_strdupa(monitorfilename);
04858       }
04859       ast_channel_unlock(qe->chan);
04860       /* Begin Monitoring */
04861       if (qe->parent->monfmt && *qe->parent->monfmt) {
04862          if (!qe->parent->montype) {
04863             const char *monexec;
04864             ast_debug(1, "Starting Monitor as requested.\n");
04865             ast_channel_lock(qe->chan);
04866             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) {
04867                which = qe->chan;
04868                monexec = monexec ? ast_strdupa(monexec) : NULL;
04869             }
04870             else
04871                which = peer;
04872             ast_channel_unlock(qe->chan);
04873             if (monitorfilename) {
04874                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
04875             } else if (qe->chan->cdr) {
04876                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
04877             } else {
04878                /* Last ditch effort -- no CDR, make up something */
04879                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04880                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
04881             }
04882             if (!ast_strlen_zero(monexec)) {
04883                ast_monitor_setjoinfiles(which, 1);
04884             }
04885          } else {
04886             mixmonapp = pbx_findapp("MixMonitor");
04887             
04888             if (mixmonapp) {
04889                ast_debug(1, "Starting MixMonitor as requested.\n");
04890                if (!monitorfilename) {
04891                   if (qe->chan->cdr) {
04892                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
04893                   } else {
04894                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04895                   }
04896                } else {
04897                   const char *m = monitorfilename;
04898                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
04899                      switch (*m) {
04900                      case '^':
04901                         if (*(m + 1) == '{')
04902                            *p = '$';
04903                         break;
04904                      case ',':
04905                         *p++ = '\\';
04906                         /* Fall through */
04907                      default:
04908                         *p = *m;
04909                      }
04910                      if (*m == '\0')
04911                         break;
04912                   }
04913                   if (p == tmpid2 + sizeof(tmpid2))
04914                      tmpid2[sizeof(tmpid2) - 1] = '\0';
04915 
04916                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
04917                }
04918 
04919                ast_channel_lock(qe->chan);
04920                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
04921                      monitor_exec = ast_strdupa(monitor_exec);
04922                }
04923                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
04924                      monitor_options = ast_strdupa(monitor_options);
04925                } else {
04926                   monitor_options = "";
04927                }
04928                ast_channel_unlock(qe->chan);
04929 
04930                if (monitor_exec) {
04931                   const char *m = monitor_exec;
04932                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
04933                      switch (*m) {
04934                      case '^':
04935                         if (*(m + 1) == '{')
04936                            *p = '$';
04937                         break;
04938                      case ',':
04939                         *p++ = '\\';
04940                         /* Fall through */
04941                      default:
04942                         *p = *m;
04943                      }
04944                      if (*m == '\0')
04945                         break;
04946                   }
04947                   if (p == meid2 + sizeof(meid2))
04948                      meid2[sizeof(meid2) - 1] = '\0';
04949 
04950                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
04951                }
04952    
04953                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
04954 
04955                if (!ast_strlen_zero(monitor_exec))
04956                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
04957                else
04958                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
04959                
04960                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
04961                /* We purposely lock the CDR so that pbx_exec does not update the application data */
04962                if (qe->chan->cdr) {
04963                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04964                }
04965                pbx_exec(qe->chan, mixmonapp, mixmonargs);
04966                if (qe->chan->cdr) {
04967                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04968                }
04969             } else {
04970                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
04971             }
04972          }
04973       }
04974       /* Drop out of the queue at this point, to prepare for next caller */
04975       leave_queue(qe);        
04976       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
04977          ast_debug(1, "app_queue: sendurl=%s.\n", url);
04978          ast_channel_sendurl(peer, url);
04979       }
04980       
04981       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
04982       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
04983       if (!ast_strlen_zero(macro)) {
04984             macroexec = ast_strdupa(macro);
04985       } else {
04986          if (qe->parent->membermacro)
04987             macroexec = ast_strdupa(qe->parent->membermacro);
04988       }
04989 
04990       if (!ast_strlen_zero(macroexec)) {
04991          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
04992          
04993          res = ast_autoservice_start(qe->chan);
04994          if (res) {
04995             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04996             res = -1;
04997          }
04998          
04999          application = pbx_findapp("Macro");
05000 
05001          if (application) {
05002             res = pbx_exec(peer, application, macroexec);
05003             ast_debug(1, "Macro exited with status %d\n", res);
05004             res = 0;
05005          } else {
05006             ast_log(LOG_ERROR, "Could not find application Macro\n");
05007             res = -1;
05008          }
05009 
05010          if (ast_autoservice_stop(qe->chan) < 0) {
05011             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05012             res = -1;
05013          }
05014       }
05015 
05016       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
05017       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
05018       if (!ast_strlen_zero(gosub)) {
05019             gosubexec = ast_strdupa(gosub);
05020       } else {
05021          if (qe->parent->membergosub)
05022             gosubexec = ast_strdupa(qe->parent->membergosub);
05023       }
05024 
05025       if (!ast_strlen_zero(gosubexec)) {
05026          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
05027          
05028          res = ast_autoservice_start(qe->chan);
05029          if (res) {
05030             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
05031             res = -1;
05032          }
05033          
05034          application = pbx_findapp("Gosub");
05035          
05036          if (application) {
05037             char *gosub_args, *gosub_argstart;
05038 
05039             /* Set where we came from */
05040             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
05041             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
05042             peer->priority = 0;
05043 
05044             gosub_argstart = strchr(gosubexec, ',');
05045             if (gosub_argstart) {
05046                const char *what_is_s = "s";
05047                *gosub_argstart = 0;
05048                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05049                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05050                   what_is_s = "~~s~~";
05051                }
05052                if (asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) {
05053                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
05054                   gosub_args = NULL;
05055                }
05056                *gosub_argstart = ',';
05057             } else {
05058                const char *what_is_s = "s";
05059                if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) &&
05060                    ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) {
05061                   what_is_s = "~~s~~";
05062                }
05063                if (asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) {
05064                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
05065                   gosub_args = NULL;
05066                }
05067             }
05068             if (gosub_args) {
05069                res = pbx_exec(peer, application, gosub_args);
05070                if (!res) {
05071                   struct ast_pbx_args args;
05072                   memset(&args, 0, sizeof(args));
05073                   args.no_hangup_chan = 1;
05074                   ast_pbx_run_args(peer, &args);
05075                }
05076                ast_free(gosub_args);
05077                ast_debug(1, "Gosub exited with status %d\n", res);
05078             } else {
05079                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
05080             }
05081          } else {
05082             ast_log(LOG_ERROR, "Could not find application Gosub\n");
05083             res = -1;
05084          }
05085       
05086          if (ast_autoservice_stop(qe->chan) < 0) {
05087             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
05088             res = -1;
05089          }
05090       }
05091 
05092       if (!ast_strlen_zero(agi)) {
05093          ast_debug(1, "app_queue: agi=%s.\n", agi);
05094          application = pbx_findapp("agi");
05095          if (application) {
05096             agiexec = ast_strdupa(agi);
05097             pbx_exec(qe->chan, application, agiexec);
05098          } else
05099             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
05100       }
05101       qe->handled++;
05102       ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, ast_channel_uniqueid(peer),
05103                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
05104 
05105       if (qe->chan->cdr) {
05106          struct ast_cdr *cdr;
05107          struct ast_cdr *newcdr;
05108 
05109          /* Only work with the last CDR in the stack*/
05110          cdr = qe->chan->cdr;
05111          while (cdr->next) {
05112             cdr = cdr->next;
05113          }
05114 
05115          /* If this CDR is not related to us add new one*/
05116          if ((strcasecmp(cdr->uniqueid, ast_channel_uniqueid(qe->chan))) &&
05117              (strcasecmp(cdr->linkedid, ast_channel_uniqueid(qe->chan))) &&
05118              (newcdr = ast_cdr_dup(cdr))) {
05119             ast_channel_lock(qe->chan);
05120             ast_cdr_init(newcdr, qe->chan);
05121             ast_cdr_reset(newcdr, 0);
05122             cdr = ast_cdr_append(cdr, newcdr);
05123             cdr = cdr->next;
05124             ast_channel_unlock(qe->chan);
05125          }
05126 
05127          if (update_cdr) {
05128             ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel));
05129          }
05130       }
05131 
05132       if (qe->parent->eventwhencalled)
05133          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
05134                "Queue: %s\r\n"
05135                "Uniqueid: %s\r\n"
05136                "Channel: %s\r\n"
05137                "Member: %s\r\n"
05138                "MemberName: %s\r\n"
05139                "Holdtime: %ld\r\n"
05140                "BridgedChannel: %s\r\n"
05141                "Ringtime: %ld\r\n"
05142                "%s",
05143                queuename, ast_channel_uniqueid(qe->chan), ast_channel_name(peer), member->interface, member->membername,
05144                (long) time(NULL) - qe->start, ast_channel_uniqueid(peer), (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
05145                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
05146       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
05147       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
05148    
05149       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
05150          queue_end_bridge->q = qe->parent;
05151          queue_end_bridge->chan = qe->chan;
05152          bridge_config.end_bridge_callback = end_bridge_callback;
05153          bridge_config.end_bridge_callback_data = queue_end_bridge;
05154          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
05155          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
05156           * to make sure to increase the refcount of this queue so it cannot be freed until we
05157           * are done with it. We remove this reference in end_bridge_callback.
05158           */
05159          queue_t_ref(qe->parent, "For bridge_config reference");
05160       }
05161 
05162       time(&callstart);
05163       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
05164       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
05165 
05166       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
05167        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
05168        * the AgentComplete manager event
05169        */
05170       ast_channel_lock(qe->chan);
05171       if (!attended_transfer_occurred(qe->chan)) {
05172          struct ast_datastore *tds;
05173 
05174          /* detect a blind transfer */
05175          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
05176             ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
05177                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
05178                (long) (time(NULL) - callstart), qe->opos);
05179             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05180          } else if (ast_check_hangup(qe->chan)) {
05181             ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "COMPLETECALLER", "%ld|%ld|%d",
05182                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05183             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
05184          } else {
05185             ast_queue_log(queuename, ast_channel_uniqueid(qe->chan), member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
05186                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
05187             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
05188          }
05189          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
05190             ast_channel_datastore_remove(qe->chan, tds);
05191          }
05192          ast_channel_unlock(qe->chan);
05193          update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
05194       } else {
05195          ast_channel_unlock(qe->chan);
05196 
05197          /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
05198          send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
05199       }
05200 
05201       if (transfer_ds) {
05202          ast_datastore_free(transfer_ds);
05203       }
05204       ast_hangup(peer);
05205       res = bridge ? bridge : 1;
05206       ao2_ref(member, -1);
05207    }
05208 out:
05209    hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
05210 
05211    return res;
05212 }

static int unload_module ( void   )  [static]

Definition at line 8625 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(), cli_queue, extension_state_cb(), queue_t_unref, queueexists_function, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queues_t_unlink, queuevar_function, and queuewaitingcount_function.

08626 {
08627    int res;
08628    struct ast_context *con;
08629    struct ao2_iterator q_iter;
08630    struct call_queue *q = NULL;
08631 
08632    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08633    res = ast_manager_unregister("QueueStatus");
08634    res |= ast_manager_unregister("Queues");
08635    res |= ast_manager_unregister("QueueRule");
08636    res |= ast_manager_unregister("QueueSummary");
08637    res |= ast_manager_unregister("QueueAdd");
08638    res |= ast_manager_unregister("QueueRemove");
08639    res |= ast_manager_unregister("QueuePause");
08640    res |= ast_manager_unregister("QueueLog");
08641    res |= ast_manager_unregister("QueuePenalty");
08642    res |= ast_unregister_application(app_aqm);
08643    res |= ast_unregister_application(app_rqm);
08644    res |= ast_unregister_application(app_pqm);
08645    res |= ast_unregister_application(app_upqm);
08646    res |= ast_unregister_application(app_ql);
08647    res |= ast_unregister_application(app);
08648    res |= ast_custom_function_unregister(&queueexists_function);
08649    res |= ast_custom_function_unregister(&queuevar_function);
08650    res |= ast_custom_function_unregister(&queuemembercount_function);
08651    res |= ast_custom_function_unregister(&queuemembercount_dep);
08652    res |= ast_custom_function_unregister(&queuememberlist_function);
08653    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08654    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08655 
08656    res |= ast_data_unregister(NULL);
08657 
08658    if (device_state_sub)
08659       ast_event_unsubscribe(device_state_sub);
08660 
08661    ast_extension_state_del(0, extension_state_cb);
08662 
08663    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08664       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08665       ast_context_destroy(con, "app_queue"); /* leave no trace */
08666    }
08667 
08668    q_iter = ao2_iterator_init(queues, 0);
08669    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08670       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08671       queue_t_unref(q, "Done with iterator");
08672    }
08673    ao2_iterator_destroy(&q_iter);
08674    ao2_ref(queues, -1);
08675    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08676    ast_unload_realtime("queue_members");
08677    return res;
08678 }

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

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

Referenced by queue_exec(), and wait_our_turn().

04009 {
04010    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
04011    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
04012    char max_penalty_str[20], min_penalty_str[20]; 
04013    /* a relative change to the penalty could put it below 0 */
04014    if (max_penalty < 0)
04015       max_penalty = 0;
04016    if (min_penalty < 0)
04017       min_penalty = 0;
04018    if (min_penalty > max_penalty)
04019       min_penalty = max_penalty;
04020    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
04021    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
04022    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
04023    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
04024    qe->max_penalty = max_penalty;
04025    qe->min_penalty = min_penalty;
04026    ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, ast_channel_name(qe->chan), qe->pr->time);
04027    qe->pr = AST_LIST_NEXT(qe->pr, list);
04028 }

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

04117 {
04118    int oldtalktime;
04119 
04120    struct member *mem;
04121    struct call_queue *qtmp;
04122    struct ao2_iterator queue_iter;
04123 
04124    if (shared_lastcall) {
04125       queue_iter = ao2_iterator_init(queues, 0);
04126       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04127          ao2_lock(qtmp);
04128          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04129             time(&mem->lastcall);
04130             mem->calls++;
04131             mem->lastqueue = q;
04132             ao2_ref(mem, -1);
04133          }
04134          ao2_unlock(qtmp);
04135          queue_t_unref(qtmp, "Done with iterator");
04136       }
04137       ao2_iterator_destroy(&queue_iter);
04138    } else {
04139       ao2_lock(q);
04140       time(&member->lastcall);
04141       member->calls++;
04142       member->lastqueue = q;
04143       ao2_unlock(q);
04144    }  
04145    ao2_lock(q);
04146    q->callscompleted++;
04147    if (callcompletedinsl)
04148       q->callscompletedinsl++;
04149    /* Calculate talktime using the same exponential average as holdtime code*/
04150    oldtalktime = q->talktime;
04151    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04152    ao2_unlock(q);
04153    return 0;
04154 }

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

Definition at line 2464 of file app_queue.c.

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

Referenced by queue_function_mem_write(), remove_from_queue(), set_member_paused(), and set_member_penalty_help_members().

02465 {
02466    int ret = -1;
02467 
02468    if (ast_strlen_zero(mem->rt_uniqueid))
02469       return ret;
02470 
02471    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02472       ret = 0;
02473 
02474    return ret;
02475 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 2478 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_strlen_zero(), member::dead, member::interface, member::membername, call_queue::members, call_queue::name, member::realtime, rt_handle_member_record(), and SENTINEL.

Referenced by find_load_queue_rt_friendly(), and queue_exec().

02479 {
02480    struct ast_config *member_config = NULL;
02481    struct member *m;
02482    char *interface = NULL;
02483    struct ao2_iterator mem_iter;
02484 
02485    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02486       /*This queue doesn't have realtime members*/
02487       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02488       return;
02489    }
02490 
02491    ao2_lock(q);
02492 
02493    /* Temporarily set realtime  members dead so we can detect deleted ones.*/
02494    mem_iter = ao2_iterator_init(q->members, 0);
02495    while ((m = ao2_iterator_next(&mem_iter))) {
02496       if (m->realtime)
02497          m->dead = 1;
02498       ao2_ref(m, -1);
02499    }
02500    ao2_iterator_destroy(&mem_iter);
02501 
02502    while ((interface = ast_category_browse(member_config, interface))) {
02503       rt_handle_member_record(q, interface, member_config);
02504    }
02505 
02506    /* Delete all realtime members that have been deleted in DB. */
02507    mem_iter = ao2_iterator_init(q->members, 0);
02508    while ((m = ao2_iterator_next(&mem_iter))) {
02509       if (m->dead) {
02510          if (ast_strlen_zero(m->membername) || !log_membername_as_agent) {
02511             ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02512          } else {
02513             ast_queue_log(q->name, "REALTIME", m->membername, "REMOVEMEMBER", "%s", "");
02514          }
02515          ao2_unlink(q->members, m);
02516       }
02517       ao2_ref(m, -1);
02518    }
02519    ao2_iterator_destroy(&mem_iter);
02520    ao2_unlock(q);
02521    ast_config_destroy(member_config);
02522 }

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

References EVENT_FLAG_AGENT, and manager_event.

Referenced by extension_state_cb(), handle_statechange(), and ring_entry().

01456 {
01457    m->status = status;
01458 
01459    if (q->maskmemberstatus)
01460       return 0;
01461 
01462    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01463       "Queue: %s\r\n"
01464       "Location: %s\r\n"
01465       "MemberName: %s\r\n"
01466       "StateInterface: %s\r\n"
01467       "Membership: %s\r\n"
01468       "Penalty: %d\r\n"
01469       "CallsTaken: %d\r\n"
01470       "LastCall: %d\r\n"
01471       "Status: %d\r\n"
01472       "Paused: %d\r\n",
01473       q->name, m->interface, m->membername, m->state_interface, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01474       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01475    );
01476 
01477    return 0;
01478 }

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

UnPauseQueueMember application.

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

05743 {
05744    char *parse;
05745    AST_DECLARE_APP_ARGS(args,
05746       AST_APP_ARG(queuename);
05747       AST_APP_ARG(interface);
05748       AST_APP_ARG(options);
05749       AST_APP_ARG(reason);
05750    );
05751 
05752    if (ast_strlen_zero(data)) {
05753       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05754       return -1;
05755    }
05756 
05757    parse = ast_strdupa(data);
05758 
05759    AST_STANDARD_APP_ARGS(args, parse);
05760 
05761    if (ast_strlen_zero(args.interface)) {
05762       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05763       return -1;
05764    }
05765 
05766    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05767       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05768       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05769       return 0;
05770    }
05771 
05772    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05773 
05774    return 0;
05775 }

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

02640 {
02641    int digitlen = strlen(qe->digits);
02642 
02643    /* Prevent possible buffer overflow */
02644    if (digitlen < sizeof(qe->digits) - 2) {
02645       qe->digits[digitlen] = digit;
02646       qe->digits[digitlen + 1] = '\0';
02647    } else {
02648       qe->digits[0] = '\0';
02649       return 0;
02650    }
02651 
02652    /* If there's no context to goto, short-circuit */
02653    if (ast_strlen_zero(qe->context))
02654       return 0;
02655 
02656    /* If the extension is bad, then reset the digits to blank */
02657    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02658       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02659       qe->digits[0] = '\0';
02660       return 0;
02661    }
02662 
02663    /* We have an exact match */
02664    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02665       qe->valid_digits = 1;
02666       /* Return 1 on a successful goto */
02667       return 1;
02668    }
02669 
02670    return 0;
02671 }

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

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

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5214 of file app_queue.c.

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

Referenced by queue_exec().

05215 {
05216    /* Don't need to hold the lock while we setup the outgoing calls */
05217    int retrywait = qe->parent->retry * 1000;
05218 
05219    int res = ast_waitfordigit(qe->chan, retrywait);
05220    if (res > 0 && !valid_exit(qe, res))
05221       res = 0;
05222 
05223    return res;
05224 }

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  update_connectedline 
) [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()
[in] update_connectedline Allow connected line and redirecting updates to pass through.
Todo:
eventually all call forward logic should be intergerated into and replaced by ast_call_forward()

Definition at line 3549 of file app_queue.c.

References ast_channel::_state, 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_accountcode(), ast_channel_call_forward(), 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_name(), ast_channel_redirecting_macro(), ast_channel_set_redirecting(), 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_caller_copy(), 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_request(), AST_STATE_UP, ast_strdup, ast_strdupa, ast_strlen_zero(), ast_verb, ast_waitfor_n(), call_queue::autopausebusy, call_queue::autopauseunavail, 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_frame_subclass::integer, callattempt::interface, member::interface, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, ast_party_id::number, queue_ent::parent, callattempt::pending_connected_update, queue_ent::pos, ast_frame::ptr, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ast_channel::redirecting, ring_one(), queue_ent::ring_when_ringing, rna(), S_OR, ast_party_connected_line::source, queue_ent::start, starttime, 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().

03550 {
03551    const char *queue = qe->parent->name;
03552    struct callattempt *o, *start = NULL, *prev = NULL;
03553    int res;
03554    int status;
03555    int numbusies = prebusies;
03556    int numnochan = 0;
03557    int stillgoing = 0;
03558    int orig = *to;
03559    struct ast_frame *f;
03560    struct callattempt *peer = NULL;
03561    struct ast_channel *winner;
03562    struct ast_channel *in = qe->chan;
03563    char on[80] = "";
03564    char membername[80] = "";
03565    long starttime = 0;
03566    long endtime = 0;
03567 #ifdef HAVE_EPOLL
03568    struct callattempt *epollo;
03569 #endif
03570    struct ast_party_connected_line connected_caller;
03571    char *inchan_name;
03572 
03573    ast_party_connected_line_init(&connected_caller);
03574 
03575    ast_channel_lock(qe->chan);
03576    inchan_name = ast_strdupa(ast_channel_name(qe->chan));
03577    ast_channel_unlock(qe->chan);
03578 
03579    starttime = (long) time(NULL);
03580 #ifdef HAVE_EPOLL
03581    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03582       if (epollo->chan)
03583          ast_poll_channel_add(in, epollo->chan);
03584    }
03585 #endif
03586    
03587    while (*to && !peer) {
03588       int numlines, retry, pos = 1;
03589       struct ast_channel *watchers[AST_MAX_WATCHERS];
03590       watchers[0] = in;
03591       start = NULL;
03592 
03593       for (retry = 0; retry < 2; retry++) {
03594          numlines = 0;
03595          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
03596             if (o->stillgoing) { /* Keep track of important channels */
03597                stillgoing = 1;
03598                if (o->chan) {
03599                   if (pos < AST_MAX_WATCHERS) {
03600                      watchers[pos++] = o->chan;
03601                   }
03602                   if (!start)
03603                      start = o;
03604                   else
03605                      prev->call_next = o;
03606                   prev = o;
03607                }
03608             }
03609             numlines++;
03610          }
03611          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
03612             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
03613             break;
03614          /* On "ringall" strategy we only move to the next penalty level
03615             when *all* ringing phones are done in the current penalty level */
03616          ring_one(qe, outgoing, &numbusies);
03617          /* and retry... */
03618       }
03619       if (pos == 1 /* not found */) {
03620          if (numlines == (numbusies + numnochan)) {
03621             ast_debug(1, "Everyone is busy at this time\n");
03622          } else {
03623             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03624          }
03625          *to = 0;
03626          return NULL;
03627       }
03628 
03629       /* Poll for events from both the incoming channel as well as any outgoing channels */
03630       winner = ast_waitfor_n(watchers, pos, to);
03631 
03632       /* Service all of the outgoing channels */
03633       for (o = start; o; o = o->call_next) {
03634          /* We go with a static buffer here instead of using ast_strdupa. Using
03635           * ast_strdupa in a loop like this one can cause a stack overflow
03636           */
03637          char ochan_name[AST_CHANNEL_NAME];
03638          if (o->chan) {
03639             ast_channel_lock(o->chan);
03640             ast_copy_string(ochan_name, ast_channel_name(o->chan), sizeof(ochan_name));
03641             ast_channel_unlock(o->chan);
03642          }
03643          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
03644             if (!peer) {
03645                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03646                if (update_connectedline) {
03647                   if (o->pending_connected_update) {
03648                      if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03649                         ast_channel_update_connected_line(in, &o->connected, NULL);
03650                      }
03651                   } else if (!o->dial_callerid_absent) {
03652                      ast_channel_lock(o->chan);
03653                      ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03654                      ast_channel_unlock(o->chan);
03655                      connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03656                      ast_channel_update_connected_line(in, &connected_caller, NULL);
03657                      ast_party_connected_line_free(&connected_caller);
03658                   }
03659                }
03660                if (o->aoc_s_rate_list) {
03661                   size_t encoded_size;
03662                   struct ast_aoc_encoded *encoded;
03663                   if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03664                      ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03665                      ast_aoc_destroy_encoded(encoded);
03666                   }
03667                }
03668                peer = o;
03669             }
03670          } else if (o->chan && (o->chan == winner)) {
03671 
03672             ast_copy_string(on, o->member->interface, sizeof(on));
03673             ast_copy_string(membername, o->member->membername, sizeof(membername));
03674 
03675             if (!ast_strlen_zero(ast_channel_call_forward(o->chan)) && !forwardsallowed) {
03676                ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, ast_channel_call_forward(o->chan));
03677                numnochan++;
03678                do_hang(o);
03679                winner = NULL;
03680                continue;
03681             } else if (!ast_strlen_zero(ast_channel_call_forward(o->chan))) {
03682                struct ast_channel *original = o->chan;
03683                char tmpchan[256];
03684                char *stuff;
03685                char *tech;
03686 
03687                ast_copy_string(tmpchan, ast_channel_call_forward(o->chan), sizeof(tmpchan));
03688                if ((stuff = strchr(tmpchan, '/'))) {
03689                   *stuff++ = '\0';
03690                   tech = tmpchan;
03691                } else {
03692                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", ast_channel_call_forward(o->chan), o->chan->context);
03693                   stuff = tmpchan;
03694                   tech = "Local";
03695                }
03696 
03697                ast_cel_report_event(in, AST_CEL_FORWARD, NULL, ast_channel_call_forward(o->chan), NULL);
03698 
03699                /* Before processing channel, go ahead and check for forwarding */
03700                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03701                /* Setup parameters */
03702                o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03703                if (!o->chan) {
03704                   ast_log(LOG_NOTICE,
03705                      "Forwarding failed to create channel to dial '%s/%s'\n",
03706                      tech, stuff);
03707                   o->stillgoing = 0;
03708                   numnochan++;
03709                } else {
03710                   struct ast_party_redirecting redirecting;
03711 
03712                   ast_channel_lock_both(o->chan, in);
03713                   ast_channel_inherit_variables(in, o->chan);
03714                   ast_channel_datastore_inherit(in, o->chan);
03715 
03716                   ast_channel_accountcode_set(o->chan, ast_channel_accountcode(in));
03717 
03718                   ast_channel_set_redirecting(o->chan, &original->redirecting, NULL);
03719                   if (!o->chan->redirecting.from.number.valid
03720                      || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03721                      /*
03722                       * The call was not previously redirected so it is
03723                       * now redirected from this number.
03724                       */
03725                      ast_party_number_free(&o->chan->redirecting.from.number);
03726                      ast_party_number_init(&o->chan->redirecting.from.number);
03727                      o->chan->redirecting.from.number.valid = 1;
03728                      o->chan->redirecting.from.number.str =
03729                         ast_strdup(S_OR(in->macroexten, in->exten));
03730                   }
03731 
03732                   o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03733 
03734                   ast_party_caller_copy(&o->chan->caller, &in->caller);
03735                   ast_party_connected_line_copy(&o->chan->connected, &original->connected);
03736 
03737                   /*
03738                    * We must unlock o->chan before calling
03739                    * ast_channel_redirecting_macro, because we put o->chan into
03740                    * autoservice there.  That is pretty much a guaranteed
03741                    * deadlock.  This is why the handling of o->chan's lock may
03742                    * seem a bit unusual here.
03743                    */
03744                   ast_party_redirecting_init(&redirecting);
03745                   ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03746                   ast_channel_unlock(o->chan);
03747                   res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0);
03748                   if (res) {
03749                      ast_channel_update_redirecting(in, &redirecting, NULL);
03750                   }
03751                   ast_party_redirecting_free(&redirecting);
03752                   ast_channel_unlock(in);
03753 
03754                   update_connectedline = 1;
03755 
03756                   if (ast_call(o->chan, stuff, 0)) {
03757                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03758                         tech, stuff);
03759                      do_hang(o);
03760                      numnochan++;
03761                   }
03762                }
03763                /* Hangup the original channel now, in case we needed it */
03764                ast_hangup(winner);
03765                continue;
03766             }
03767             f = ast_read(winner);
03768             if (f) {
03769                if (f->frametype == AST_FRAME_CONTROL) {
03770                   switch (f->subclass.integer) {
03771                   case AST_CONTROL_ANSWER:
03772                      /* This is our guy if someone answered. */
03773                      if (!peer) {
03774                         ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03775                         if (update_connectedline) {
03776                            if (o->pending_connected_update) {
03777                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03778                                  ast_channel_update_connected_line(in, &o->connected, NULL);
03779                               }
03780                            } else if (!o->dial_callerid_absent) {
03781                               ast_channel_lock(o->chan);
03782                               ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03783                               ast_channel_unlock(o->chan);
03784                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03785                               ast_channel_update_connected_line(in, &connected_caller, NULL);
03786                               ast_party_connected_line_free(&connected_caller);
03787                            }
03788                         }
03789                         if (o->aoc_s_rate_list) {
03790                            size_t encoded_size;
03791                            struct ast_aoc_encoded *encoded;
03792                            if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03793                               ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03794                               ast_aoc_destroy_encoded(encoded);
03795                            }
03796                         }
03797                         peer = o;
03798                      }
03799                      break;
03800                   case AST_CONTROL_BUSY:
03801                      ast_verb(3, "%s is busy\n", ochan_name);
03802                      if (in->cdr)
03803                         ast_cdr_busy(in->cdr);
03804                      do_hang(o);
03805                      endtime = (long) time(NULL);
03806                      endtime -= starttime;
03807                      rna(endtime * 1000, qe, on, membername, qe->parent->autopausebusy);
03808                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03809                         if (qe->parent->timeoutrestart)
03810                            *to = orig;
03811                         /* Have enough time for a queue member to answer? */
03812                         if (*to > 500) {
03813                            ring_one(qe, outgoing, &numbusies);
03814                            starttime = (long) time(NULL);
03815                         }
03816                      }
03817                      numbusies++;
03818                      break;
03819                   case AST_CONTROL_CONGESTION:
03820                      ast_verb(3, "%s is circuit-busy\n", ochan_name);
03821                      if (in->cdr)
03822                         ast_cdr_busy(in->cdr);
03823                      endtime = (long) time(NULL);
03824                      endtime -= starttime;
03825                      rna(endtime * 1000, qe, on, membername, qe->parent->autopauseunavail);
03826                      do_hang(o);
03827                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03828                         if (qe->parent->timeoutrestart)
03829                            *to = orig;
03830                         if (*to > 500) {
03831                            ring_one(qe, outgoing, &numbusies);
03832                            starttime = (long) time(NULL);
03833                         }
03834                      }
03835                      numbusies++;
03836                      break;
03837                   case AST_CONTROL_RINGING:
03838                      ast_verb(3, "%s is ringing\n", ochan_name);
03839 
03840                      /* Start ring indication when the channel is ringing, if specified */
03841                      if (qe->ring_when_ringing) {
03842                         ast_moh_stop(qe->chan);
03843                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
03844                      }
03845                      break;
03846                   case AST_CONTROL_OFFHOOK:
03847                      /* Ignore going off hook */
03848                      break;
03849                   case AST_CONTROL_CONNECTED_LINE:
03850                      if (!update_connectedline) {
03851                         ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03852                      } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03853                         struct ast_party_connected_line connected;
03854                         ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03855                         ast_party_connected_line_set_init(&connected, &o->connected);
03856                         ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03857                         ast_party_connected_line_set(&o->connected, &connected, NULL);
03858                         ast_party_connected_line_free(&connected);
03859                         o->pending_connected_update = 1;
03860                      } else {
03861                         if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03862                            ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03863                         }
03864                      }
03865                      break;
03866                   case AST_CONTROL_AOC:
03867                      {
03868                         struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03869                         if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03870                            ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03871                            o->aoc_s_rate_list = decoded;
03872                         } else {
03873                            ast_aoc_destroy_decoded(decoded);
03874                         }
03875                      }
03876                      break;
03877                   case AST_CONTROL_REDIRECTING:
03878                      if (!update_connectedline) {
03879                         ast_verb(3, "Redirecting update to %s prevented\n", inchan_name);
03880                      } else if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03881                         ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name);
03882                         if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
03883                            ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
03884                         }
03885                      }
03886                      break;
03887                   default:
03888                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
03889                      break;
03890                   }
03891                }
03892                ast_frfree(f);
03893             } else { /* ast_read() returned NULL */
03894                endtime = (long) time(NULL) - starttime;
03895                rna(endtime * 1000, qe, on, membername, 1);
03896                do_hang(o);
03897                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03898                   if (qe->parent->timeoutrestart)
03899                      *to = orig;
03900                   if (*to > 500) {
03901                      ring_one(qe, outgoing, &numbusies);
03902                      starttime = (long) time(NULL);
03903                   }
03904                }
03905             }
03906          }
03907       }
03908 
03909       /* If we received an event from the caller, deal with it. */
03910       if (winner == in) {
03911          f = ast_read(in);
03912          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
03913             /* Got hung up */
03914             *to = -1;
03915             if (f) {
03916                if (f->data.uint32) {
03917                   in->hangupcause = f->data.uint32;
03918                }
03919                ast_frfree(f);
03920             }
03921             return NULL;
03922          }
03923          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
03924             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
03925             *to = 0;
03926             ast_frfree(f);
03927             return NULL;
03928          }
03929          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
03930             ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
03931             *to = 0;
03932             *digit = f->subclass.integer;
03933             ast_frfree(f);
03934             return NULL;
03935          }
03936          ast_frfree(f);
03937       }
03938       if (!*to) {
03939          for (o = start; o; o = o->call_next)
03940             rna(orig, qe, o->interface, o->member->membername, 1);
03941       }
03942    }
03943 
03944 #ifdef HAVE_EPOLL
03945    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03946       if (epollo->chan)
03947          ast_poll_channel_del(in, epollo->chan);
03948    }
03949 #endif
03950 
03951    return peer;
03952 }

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

References call_queue::announcefrequency, ast_channel_uniqueid(), 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, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, penalty_rule::time, update_qe_rule(), and valid_exit().

Referenced by queue_exec().

04041 {
04042    int res = 0;
04043 
04044    /* This is the holding pen for callers 2 through maxlen */
04045    for (;;) {
04046 
04047       if (is_our_turn(qe))
04048          break;
04049 
04050       /* If we have timed out, break out */
04051       if (qe->expire && (time(NULL) >= qe->expire)) {
04052          *reason = QUEUE_TIMEOUT;
04053          break;
04054       }
04055 
04056       if (qe->parent->leavewhenempty) {
04057          int status = 0;
04058 
04059          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
04060             *reason = QUEUE_LEAVEEMPTY;
04061             ast_queue_log(qe->parent->name, ast_channel_uniqueid(qe->chan), "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04062             leave_queue(qe);
04063             break;
04064          }
04065       }
04066 
04067       /* Make a position announcement, if enabled */
04068       if (qe->parent->announcefrequency &&
04069          (res = say_position(qe,ringing)))
04070          break;
04071 
04072       /* If we have timed out, break out */
04073       if (qe->expire && (time(NULL) >= qe->expire)) {
04074          *reason = QUEUE_TIMEOUT;
04075          break;
04076       }
04077 
04078       /* Make a periodic announcement, if enabled */
04079       if (qe->parent->periodicannouncefrequency &&
04080          (res = say_periodic_announcement(qe,ringing)))
04081          break;
04082       
04083       /* see if we need to move to the next penalty level for this queue */
04084       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
04085          update_qe_rule(qe);
04086       }
04087 
04088       /* If we have timed out, break out */
04089       if (qe->expire && (time(NULL) >= qe->expire)) {
04090          *reason = QUEUE_TIMEOUT;
04091          break;
04092       }
04093       
04094       /* Wait a second before checking again */
04095       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
04096          if (res > 0 && !valid_exit(qe, res))
04097             res = 0;
04098          else
04099             break;
04100       }
04101       
04102       /* If we have timed out, break out */
04103       if (qe->expire && (time(NULL) >= qe->expire)) {
04104          *reason = QUEUE_TIMEOUT;
04105          break;
04106       }
04107    }
04108 
04109    return res;
04110 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", } [static]

Definition at line 8777 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 916 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 918 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 922 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 926 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 920 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 924 of file app_queue.c.

Definition at line 8777 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 940 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

int check_state_unknown = 0 [static]

queues.conf [general] option

Definition at line 961 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 8344 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 949 of file app_queue.c.

Referenced by load_pbx().

Definition at line 900 of file app_queue.c.

int log_membername_as_agent = 0 [static]

queues.conf [general] option

Definition at line 958 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 943 of file app_queue.c.

int negative_penalty_invalid = 0 [static]

queues.conf [general] option

Definition at line 955 of file app_queue.c.

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

Persistent Members astdb family.

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

Initial value:

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

Definition at line 8621 of file app_queue.c.

Referenced by load_module().

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

Referenced by attended_transfer_occurred(), queue_transfer_fixup(), setup_transfer_datastore(), and try_calling().

Initial value:

 {
   .name = "QUEUE_EXISTS",
   .read = queue_function_exists,
}

Definition at line 6726 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

 {
   .name = "QUEUE_MEMBER_COUNT",
   .read = queue_function_qac_dep,
}

Definition at line 6742 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

 {
   .name = "QUEUE_MEMBER",
   .read = queue_function_mem_read,
   .write = queue_function_mem_write,
}

Definition at line 6736 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

 {
   .name = "QUEUE_MEMBER_LIST",
   .read = queue_function_queuememberlist,
}

Definition at line 6752 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

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

Definition at line 6757 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Initial value:

Definition at line 8616 of file app_queue.c.

Initial value:

 {
   .name = "QUEUE_VARIABLES",
   .read = queue_function_var,
}

Definition at line 6731 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:

 {
   .name = "QUEUE_WAITING_COUNT",
   .read = queue_function_queuewaitingcount,
}

Definition at line 6747 of file app_queue.c.

Referenced by load_module(), and unload_module().

const char qum_cmd_usage[] [static]

Initial value:

"Usage: queue unpause member <channel> in <queue> reason <reason>\n"

Definition at line 8338 of file app_queue.c.

int shared_lastcall = 1 [static]

queues.conf [general] option

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

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 937 of file app_queue.c.


Generated on Sat Feb 11 06:33:55 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6