Wed Apr 6 11:29:50 2011

Asterisk developer's documentation


app_queue.c File Reference

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

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

Go to the source code of this file.

Data Structures

struct  autopause
struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  member
struct  penalty_rule
struct  queue_end_bridge
struct  queue_ent
struct  queue_transfer_ds
struct  rule_list
struct  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
}
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 (char *context, char *exten, enum ast_extension_states state, void *data)
static int extensionstate2devicestate (int state)
 Helper function which converts from extension state to device state values.
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
 Iterate through queue's member list and delete them.
static int get_member_penalty (char *queuename, char *interface)
static int get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions)
 Check if members are available.
static int get_queue_member_status (struct member *cur)
 Return the current state of a member.
static char * handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_reset (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int handle_statechange (void *datap)
 set a member's status based on device state of that member's interface
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere)
 Hang up a list of outgoing calls.
static void init_queue (struct call_queue *q)
 Initialize Queue default values.
static void insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 Insert the 'new' entry after the 'prev' entry of queue 'q'.
static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
 Change queue penalty by adding rule.
static const char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int is_our_turn (struct queue_ent *qe)
 Check if we should start attempting to call queue members.
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, int position)
static int kill_dead_members (void *obj, void *arg, int flags)
static int kill_dead_queues (void *obj, void *arg, int flags)
static void leave_queue (struct queue_ent *qe)
 Caller leaving queue.
static int load_module (void)
static struct call_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_queue_log_custom (struct mansession *s, const struct message *m)
static int manager_queue_member_penalty (struct mansession *s, const struct message *m)
static int manager_queue_reload (struct mansession *s, const struct message *m)
static int manager_queue_reset (struct mansession *s, const struct message *m)
static int manager_queue_rule_show (struct mansession *s, const struct message *m)
static int manager_queues_show (struct mansession *s, const struct message *m)
static int manager_queues_status (struct mansession *s, const struct message *m)
 Queue status info via AMI.
static int manager_queues_summary (struct mansession *s, const struct message *m)
 Summary of queue info via the AMI.
static int manager_remove_queue_member (struct mansession *s, const struct message *m)
static int mark_dead_and_unfound (void *obj, void *arg, int flags)
static int mark_member_dead (void *obj, void *arg, int flags)
static 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_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.
static int queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.
static int queue_function_qac (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get number either busy / free / ready or total members of a specific queue.
static int queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get the total number of members in a specific queue (Deprecated).
static int queue_function_queuememberlist (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue.
static int queue_function_queuewaitingcount (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue.
static int queue_function_var (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 create interface var with all queue details.
static int queue_hash_cb (const void *obj, const int flags)
static 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, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
 Find rt member record to update otherwise create one.
static int say_periodic_announcement (struct queue_ent *qe, int ringing)
 Playback announcement to queued members if peroid has elapsed.
static int say_position (struct queue_ent *qe, int ringing)
static void send_agent_complete (const struct queue_ent *qe, const char *queuename, const struct ast_channel *peer, const struct member *member, time_t callstart, char *vars, size_t vars_len, enum agent_complete_reason rsn)
 Send out AMI message with member call completion status information.
static int set_member_paused (const char *queuename, const char *interface, const char *reason, int paused)
static int set_member_penalty (const char *queuename, const char *interface, int penalty)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
 sets the QUEUESTATUS channel variable
static void set_queue_variables (struct call_queue *q, struct ast_channel *chan)
 Set variables of queue.
static struct ast_datastoresetup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
 create a datastore for storing relevant info to log attended transfers in the queue_log
static int store_next_lin (struct queue_ent *qe, struct callattempt *outgoing)
 Search for best metric and add to Linear queue.
static int store_next_rr (struct queue_ent *qe, struct callattempt *outgoing)
 Search for best metric and add to Round Robbin queue.
static int strat2int (const char *strategy)
static int try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
 A large function which calls members, updates statistics, and bridges the caller and a member.
static int unload_module (void)
static void update_qe_rule (struct queue_ent *qe)
 update rules for queues
static int update_queue (struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime)
 update the queue status
static int update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value)
static void update_realtime_members (struct call_queue *q)
static int update_status (struct call_queue *q, struct member *m, const int status)
 set a member's status based on device state of that member's state_interface.
static int upqm_exec (struct ast_channel *chan, const char *data)
 UnPauseQueueMember application.
static int valid_exit (struct queue_ent *qe, char digit)
 Check for valid exit from queue via goto.
static char * vars2manager (struct ast_channel *chan, char *vars, size_t len)
 convert "\n" to "\nVariable: " ready for manager to use
static int wait_a_bit (struct queue_ent *qe)
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int 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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 struct ast_cli_entry cli_queue []
static struct ast_event_subdevice_state_sub
 Subscription to device state change events.
static struct ast_taskprocessordevicestate_tps
static int montype_default = 0
 queues.conf [general] option
static const char *const pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static struct ast_data_entry queue_data_providers []
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queueexists_function
static struct ast_custom_function queuemembercount_dep
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpenalty_function
static struct ao2_containerqueues
static struct ast_data_handler queues_data_provider
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 1
 queues.conf [general] option
static struct strategy strategies []
static int update_cdr = 0
 queues.conf [general] option
static int use_weight = 0
 queues.conf per-queue weight option


Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>
Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).

These features added by David C. Troy <dave@toad.net>:

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

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 901 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 916 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 915 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 914 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 913 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 3249 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 7805 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 7870 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 725 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 731 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 754 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 902 of file app_queue.c.

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

#define queue_t_ref ( a,
 )     queue_ref(a)

Definition at line 1114 of file app_queue.c.

Referenced by leave_queue().

#define queue_t_unref ( a,
 )     queue_unref(a)

Definition at line 1115 of file app_queue.c.

Referenced by __queues_show(), add_to_queue(), alloc_queue(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), extension_state_cb(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), leave_queue(), manager_queues_status(), manager_queues_summary(), queue_function_exists(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().

#define queues_t_link ( c,
q,
tag   )     ao2_t_link(c,q,tag)

Definition at line 1116 of file app_queue.c.

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

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

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 734 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOSUCHQUEUE   (-3)

No such queue

Definition at line 736 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOT_DYNAMIC   (-4)

Member is not dynamic

Definition at line 737 of file app_queue.c.

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

#define RES_OKAY   0

Action completed

Definition at line 733 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OUTOFMEMORY   (-2)

Out of memory

Definition at line 735 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), and reload_queue_members().


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 

Definition at line 676 of file app_queue.c.

anonymous enum

Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 686 of file app_queue.c.

00686      {
00687      QUEUE_AUTOPAUSE_OFF = 0,
00688      QUEUE_AUTOPAUSE_ON,
00689      QUEUE_AUTOPAUSE_ALL
00690 };

enum agent_complete_reason

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3947 of file app_queue.c.

03947                            {
03948    CALLER,
03949    AGENT,
03950    TRANSFER
03951 };

enum empty_conditions

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

00888                       {
00889    QUEUE_EMPTY_PENALTY = (1 << 0),
00890    QUEUE_EMPTY_PAUSED = (1 << 1),
00891    QUEUE_EMPTY_INUSE = (1 << 2),
00892    QUEUE_EMPTY_RINGING = (1 << 3),
00893    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
00894    QUEUE_EMPTY_INVALID = (1 << 5),
00895    QUEUE_EMPTY_UNKNOWN = (1 << 6),
00896    QUEUE_EMPTY_WRAPUP = (1 << 7),
00897 };

enum queue_reload_mask

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 692 of file app_queue.c.

00692                        {
00693    QUEUE_RELOAD_PARAMETERS = (1 << 0),
00694    QUEUE_RELOAD_MEMBER = (1 << 1),
00695    QUEUE_RELOAD_RULES = (1 << 2),
00696    QUEUE_RESET_STATS = (1 << 3),
00697 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 777 of file app_queue.c.

00777                   {
00778    QUEUE_UNKNOWN = 0,
00779    QUEUE_TIMEOUT = 1,
00780    QUEUE_JOINEMPTY = 2,
00781    QUEUE_LEAVEEMPTY = 3,
00782    QUEUE_JOINUNAVAIL = 4,
00783    QUEUE_LEAVEUNAVAIL = 5,
00784    QUEUE_FULL = 6,
00785    QUEUE_CONTINUE = 7,
00786 };

enum queue_timeout_priority

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 802 of file app_queue.c.

00802                             {
00803    TIMEOUT_PRIORITY_APP,
00804    TIMEOUT_PRIORITY_CONF,
00805 };


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

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

Referenced by manager_queues_show(), and queue_show().

06718 {
06719    struct call_queue *q;
06720    struct ast_str *out = ast_str_alloca(240);
06721    int found = 0;
06722    time_t now = time(NULL);
06723    struct ao2_iterator queue_iter;
06724    struct ao2_iterator mem_iter;
06725 
06726    if (argc != 2 && argc != 3)
06727       return CLI_SHOWUSAGE;
06728 
06729    if (argc == 3) { /* specific queue */
06730       if ((q = load_realtime_queue(argv[2]))) {
06731          queue_t_unref(q, "Done with temporary pointer");
06732       }
06733    } else if (ast_check_realtime("queues")) {
06734       /* This block is to find any queues which are defined in realtime but
06735        * which have not yet been added to the in-core container
06736        */
06737       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
06738       char *queuename;
06739       if (cfg) {
06740          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
06741             if ((q = load_realtime_queue(queuename))) {
06742                queue_t_unref(q, "Done with temporary pointer");
06743             }
06744          }
06745          ast_config_destroy(cfg);
06746       }
06747    }
06748 
06749    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
06750    ao2_lock(queues);
06751    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06752       float sl;
06753       struct call_queue *realtime_queue = NULL;
06754 
06755       ao2_lock(q);
06756       /* This check is to make sure we don't print information for realtime
06757        * queues which have been deleted from realtime but which have not yet
06758        * been deleted from the in-core container
06759        */
06760       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
06761          ao2_unlock(q);
06762          queue_t_unref(q, "Done with iterator");
06763          continue;
06764       } else if (q->realtime) {
06765          queue_t_unref(realtime_queue, "Queue is already in memory");
06766       }
06767       if (argc == 3 && strcasecmp(q->name, argv[2])) {
06768          ao2_unlock(q);
06769          queue_t_unref(q, "Done with iterator");
06770          continue;
06771       }
06772       found = 1;
06773 
06774       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
06775       if (q->maxlen)
06776          ast_str_append(&out, 0, "%d", q->maxlen);
06777       else
06778          ast_str_append(&out, 0, "unlimited");
06779       sl = 0;
06780       if (q->callscompleted > 0)
06781          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06782       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
06783          int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
06784          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
06785       do_print(s, fd, ast_str_buffer(out));
06786       if (!ao2_container_count(q->members))
06787          do_print(s, fd, "   No Members");
06788       else {
06789          struct member *mem;
06790 
06791          do_print(s, fd, "   Members: ");
06792          mem_iter = ao2_iterator_init(q->members, 0);
06793          while ((mem = ao2_iterator_next(&mem_iter))) {
06794             ast_str_set(&out, 0, "      %s", mem->membername);
06795             if (strcasecmp(mem->membername, mem->interface)) {
06796                ast_str_append(&out, 0, " (%s)", mem->interface);
06797             }
06798             if (mem->penalty)
06799                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
06800             ast_str_append(&out, 0, "%s%s%s (%s)",
06801                mem->dynamic ? " (dynamic)" : "",
06802                mem->realtime ? " (realtime)" : "",
06803                mem->paused ? " (paused)" : "",
06804                ast_devstate2str(mem->status));
06805             if (mem->calls)
06806                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
06807                   mem->calls, (long) (time(NULL) - mem->lastcall));
06808             else
06809                ast_str_append(&out, 0, " has taken no calls yet");
06810             do_print(s, fd, ast_str_buffer(out));
06811             ao2_ref(mem, -1);
06812          }
06813          ao2_iterator_destroy(&mem_iter);
06814       }
06815       if (!q->head)
06816          do_print(s, fd, "   No Callers");
06817       else {
06818          struct queue_ent *qe;
06819          int pos = 1;
06820 
06821          do_print(s, fd, "   Callers: ");
06822          for (qe = q->head; qe; qe = qe->next) {
06823             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
06824                pos++, qe->chan->name, (long) (now - qe->start) / 60,
06825                (long) (now - qe->start) % 60, qe->prio);
06826             do_print(s, fd, ast_str_buffer(out));
06827          }
06828       }
06829       do_print(s, fd, ""); /* blank line between entries */
06830       ao2_unlock(q);
06831       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
06832    }
06833    ao2_iterator_destroy(&queue_iter);
06834    ao2_unlock(queues);
06835    if (!found) {
06836       if (argc == 3)
06837          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
06838       else
06839          ast_str_set(&out, 0, "No queues.");
06840       do_print(s, fd, ast_str_buffer(out));
06841    }
06842    return CLI_SUCCESS;
06843 }

static void __reg_module ( void   )  [static]

Definition at line 8205 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 8205 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 5037 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, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), manager_event, call_queue::membercount, 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().

05038 {
05039    struct call_queue *q;
05040    struct member *new_member, *old_member;
05041    int res = RES_NOSUCHQUEUE;
05042 
05043    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05044     * short-circuits if the queue is already in memory. */
05045    if (!(q = load_realtime_queue(queuename)))
05046       return res;
05047 
05048    ao2_lock(queues);
05049 
05050    ao2_lock(q);
05051    if ((old_member = interface_exists(q, interface)) == NULL) {
05052       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05053          new_member->dynamic = 1;
05054          ao2_link(q->members, new_member);
05055          q->membercount++;
05056          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05057             "Queue: %s\r\n"
05058             "Location: %s\r\n"
05059             "MemberName: %s\r\n"
05060             "Membership: %s\r\n"
05061             "Penalty: %d\r\n"
05062             "CallsTaken: %d\r\n"
05063             "LastCall: %d\r\n"
05064             "Status: %d\r\n"
05065             "Paused: %d\r\n",
05066             q->name, new_member->interface, new_member->membername,
05067             "dynamic",
05068             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05069             new_member->status, new_member->paused);
05070          
05071          ao2_ref(new_member, -1);
05072          new_member = NULL;
05073 
05074          if (dump)
05075             dump_queue_members(q);
05076          
05077          res = RES_OKAY;
05078       } else {
05079          res = RES_OUTOFMEMORY;
05080       }
05081    } else {
05082       ao2_ref(old_member, -1);
05083       res = RES_EXISTS;
05084    }
05085    ao2_unlock(q);
05086    ao2_unlock(queues);
05087    queue_t_unref(q, "Expiring temporary reference");
05088 
05089    return res;
05090 }

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

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

02009 {
02010    struct call_queue *q;
02011 
02012    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02013       if (ast_string_field_init(q, 64)) {
02014          queue_t_unref(q, "String field allocation failed");
02015          return NULL;
02016       }
02017       ast_string_field_set(q, name, queuename);
02018    }
02019    return q;
02020 }

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

AddQueueMember application.

Definition at line 5476 of file app_queue.c.

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

Referenced by load_module().

05477 {
05478    int res=-1;
05479    char *parse, *temppos = NULL;
05480    AST_DECLARE_APP_ARGS(args,
05481       AST_APP_ARG(queuename);
05482       AST_APP_ARG(interface);
05483       AST_APP_ARG(penalty);
05484       AST_APP_ARG(options);
05485       AST_APP_ARG(membername);
05486       AST_APP_ARG(state_interface);
05487    );
05488    int penalty = 0;
05489 
05490    if (ast_strlen_zero(data)) {
05491       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05492       return -1;
05493    }
05494 
05495    parse = ast_strdupa(data);
05496 
05497    AST_STANDARD_APP_ARGS(args, parse);
05498 
05499    if (ast_strlen_zero(args.interface)) {
05500       args.interface = ast_strdupa(chan->name);
05501       temppos = strrchr(args.interface, '-');
05502       if (temppos)
05503          *temppos = '\0';
05504    }
05505 
05506    if (!ast_strlen_zero(args.penalty)) {
05507       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05508          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05509          penalty = 0;
05510       }
05511    }
05512 
05513    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05514    case RES_OKAY:
05515       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
05516       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05517       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05518       res = 0;
05519       break;
05520    case RES_EXISTS:
05521       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
05522       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
05523       res = 0;
05524       break;
05525    case RES_NOSUCHQUEUE:
05526       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
05527       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
05528       res = 0;
05529       break;
05530    case RES_OUTOFMEMORY:
05531       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
05532       break;
05533    }
05534 
05535    return res;
05536 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

04052 {
04053    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04054 }

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

Definition at line 1073 of file app_queue.c.

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

Referenced by queue_set_param().

01074 {
01075    int x;
01076    /*This 'double check' that default value is OFF */
01077    if (ast_strlen_zero(autopause))
01078       return QUEUE_AUTOPAUSE_OFF;
01079 
01080    /*This 'double check' is to ensure old values works */
01081    if(ast_true(autopause))
01082       return QUEUE_AUTOPAUSE_ON;
01083 
01084    for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) {
01085       if (!strcasecmp(autopause, autopausesmodes[x].name))
01086          return autopausesmodes[x].autopause;
01087    }
01088 
01089    /*This 'double check' that default value is OFF */
01090    return QUEUE_AUTOPAUSE_OFF;
01091 }

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

References ast_debug, ast_log(), ast_random(), member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, 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, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

03881 {
03882    /* disregarding penalty on too few members? */
03883    unsigned char usepenalty = (q->membercount <= q->penaltymemberslimit) ? 0 : 1;
03884 
03885    if (usepenalty) {
03886       if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
03887          (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
03888          return -1;
03889       }
03890    } else {
03891       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
03892            q->membercount, q->penaltymemberslimit);
03893    }
03894 
03895    switch (q->strategy) {
03896    case QUEUE_STRATEGY_RINGALL:
03897       /* Everyone equal, except for penalty */
03898       tmp->metric = mem->penalty * 1000000 * usepenalty;
03899       break;
03900    case QUEUE_STRATEGY_LINEAR:
03901       if (pos < qe->linpos) {
03902          tmp->metric = 1000 + pos;
03903       } else {
03904          if (pos > qe->linpos)
03905             /* Indicate there is another priority */
03906             qe->linwrapped = 1;
03907          tmp->metric = pos;
03908       }
03909       tmp->metric += mem->penalty * 1000000 * usepenalty;
03910       break;
03911    case QUEUE_STRATEGY_RRMEMORY:
03912       if (pos < q->rrpos) {
03913          tmp->metric = 1000 + pos;
03914       } else {
03915          if (pos > q->rrpos)
03916             /* Indicate there is another priority */
03917             q->wrapped = 1;
03918          tmp->metric = pos;
03919       }
03920       tmp->metric += mem->penalty * 1000000 * usepenalty;
03921       break;
03922    case QUEUE_STRATEGY_RANDOM:
03923       tmp->metric = ast_random() % 1000;
03924       tmp->metric += mem->penalty * 1000000 * usepenalty;
03925       break;
03926    case QUEUE_STRATEGY_WRANDOM:
03927       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03928       break;
03929    case QUEUE_STRATEGY_FEWESTCALLS:
03930       tmp->metric = mem->calls;
03931       tmp->metric += mem->penalty * 1000000 * usepenalty;
03932       break;
03933    case QUEUE_STRATEGY_LEASTRECENT:
03934       if (!mem->lastcall)
03935          tmp->metric = 0;
03936       else
03937          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03938       tmp->metric += mem->penalty * 1000000 * usepenalty;
03939       break;
03940    default:
03941       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03942       break;
03943    }
03944    return 0;
03945 }

static void callattempt_free ( struct callattempt doomed  )  [static]

Definition at line 2663 of file app_queue.c.

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

Referenced by hangupcalls().

02664 {
02665    if (doomed->member) {
02666       ao2_ref(doomed->member, -1);
02667    }
02668    ast_party_connected_line_free(&doomed->connected);
02669    ast_free(doomed);
02670 }

static void clear_queue ( struct call_queue q  )  [static]

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

01574 {
01575    q->holdtime = 0;
01576    q->callscompleted = 0;
01577    q->callsabandoned = 0;
01578    q->callscompletedinsl = 0;
01579    q->talktime = 0;
01580 
01581    if (q->members) {
01582       struct member *mem;
01583       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01584       while ((mem = ao2_iterator_next(&mem_iter))) {
01585          mem->calls = 0;
01586          mem->lastcall = 0;
01587          ao2_ref(mem, -1);
01588       }
01589       ao2_iterator_destroy(&mem_iter);
01590    }
01591 }

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 6658 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, and queue_t_unref.

Referenced by reload_handler().

06659 {
06660    struct call_queue *q;
06661    struct ao2_iterator queue_iter = ao2_iterator_init(queues, 0);
06662    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06663       ao2_lock(q);
06664       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
06665          clear_queue(q);
06666       ao2_unlock(q);
06667       queue_t_unref(q, "Done with iterator");
06668    }
06669    ao2_iterator_destroy(&queue_iter);
06670    return 0;
06671 }

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

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

02744 {
02745    struct call_queue *q;
02746    struct member *mem;
02747    int found = 0;
02748    struct ao2_iterator queue_iter;
02749    
02750    /* q's lock and rq's lock already set by try_calling()
02751     * to solve deadlock */
02752    queue_iter = ao2_iterator_init(queues, 0);
02753    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
02754       if (q == rq) { /* don't check myself, could deadlock */
02755          queue_t_unref(q, "Done with iterator");
02756          continue;
02757       }
02758       ao2_lock(q);
02759       if (q->count && q->members) {
02760          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02761             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02762             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02763                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);
02764                found = 1;
02765             }
02766             ao2_ref(mem, -1);
02767          }
02768       }
02769       ao2_unlock(q);
02770       queue_t_unref(q, "Done with iterator");
02771       if (found) {
02772          break;
02773       }
02774    }
02775    ao2_iterator_destroy(&queue_iter);
02776    return found;
02777 }

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

Definition at line 6845 of file app_queue.c.

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

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

06846 {
06847    struct call_queue *q;
06848    char *ret = NULL;
06849    int which = 0;
06850    int wordlen = strlen(word);
06851    struct ao2_iterator queue_iter;
06852 
06853    queue_iter = ao2_iterator_init(queues, 0);
06854    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06855       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
06856          ret = ast_strdup(q->name);
06857          queue_t_unref(q, "Done with iterator");
06858          break;
06859       }
06860       queue_t_unref(q, "Done with iterator");
06861    }
06862    ao2_iterator_destroy(&queue_iter);
06863 
06864    return ret;
06865 }

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

Definition at line 7277 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07278 {
07279    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07280    switch (pos) {
07281    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07282       return NULL;
07283    case 4: /* only one possible match, "to" */
07284       return state == 0 ? ast_strdup("to") : NULL;
07285    case 5: /* <queue> */
07286       return complete_queue(line, word, pos, state);
07287    case 6: /* only one possible match, "penalty" */
07288       return state == 0 ? ast_strdup("penalty") : NULL;
07289    case 7:
07290       if (state < 100) {      /* 0-99 */
07291          char *num;
07292          if ((num = ast_malloc(3))) {
07293             sprintf(num, "%d", state);
07294          }
07295          return num;
07296       } else {
07297          return NULL;
07298       }
07299    case 8: /* only one possible match, "as" */
07300       return state == 0 ? ast_strdup("as") : NULL;
07301    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
07302       return NULL;
07303    default:
07304       return NULL;
07305    }
07306 }

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

Definition at line 7498 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

07499 {
07500    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
07501    switch (pos) {
07502    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
07503       return NULL;
07504    case 4:  /* only one possible match, "queue" */
07505       return state == 0 ? ast_strdup("queue") : NULL;
07506    case 5:  /* <queue> */
07507       return complete_queue(line, word, pos, state);
07508    case 6: /* "reason" */
07509       return state == 0 ? ast_strdup("reason") : NULL;
07510    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
07511       return NULL;
07512    default:
07513       return NULL;
07514    }
07515 }

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

Definition at line 7407 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, and queue_t_unref.

Referenced by handle_queue_remove_member().

07408 {
07409    int which = 0;
07410    struct call_queue *q;
07411    struct member *m;
07412    struct ao2_iterator queue_iter;
07413    struct ao2_iterator mem_iter;
07414    int wordlen = strlen(word);
07415 
07416    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
07417    if (pos > 5 || pos < 3)
07418       return NULL;
07419    if (pos == 4)   /* only one possible match, 'from' */
07420       return (state == 0 ? ast_strdup("from") : NULL);
07421 
07422    if (pos == 5)   /* No need to duplicate code */
07423       return complete_queue(line, word, pos, state);
07424 
07425    /* here is the case for 3, <member> */
07426    queue_iter = ao2_iterator_init(queues, 0);
07427    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07428       ao2_lock(q);
07429       mem_iter = ao2_iterator_init(q->members, 0);
07430       while ((m = ao2_iterator_next(&mem_iter))) {
07431          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
07432             char *tmp;
07433             ao2_unlock(q);
07434             tmp = ast_strdup(m->interface);
07435             ao2_ref(m, -1);
07436             queue_t_unref(q, "Done with iterator, returning interface name");
07437             ao2_iterator_destroy(&mem_iter);
07438             ao2_iterator_destroy(&queue_iter);
07439             return tmp;
07440          }
07441          ao2_ref(m, -1);
07442       }
07443       ao2_iterator_destroy(&mem_iter);
07444       ao2_unlock(q);
07445       queue_t_unref(q, "Done with iterator");
07446    }
07447    ao2_iterator_destroy(&queue_iter);
07448 
07449    return NULL;
07450 }

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

Definition at line 7631 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

07632 {
07633    int which = 0;
07634    struct rule_list *rl_iter;
07635    int wordlen = strlen(word);
07636    char *ret = NULL;
07637    if (pos != 3) /* Wha? */ {
07638       return NULL;
07639    }
07640 
07641    AST_LIST_LOCK(&rule_lists);
07642    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07643       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
07644          ret = ast_strdup(rl_iter->name);
07645          break;
07646       }
07647    }
07648    AST_LIST_UNLOCK(&rule_lists);
07649 
07650    return ret;
07651 }

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

Definition at line 7568 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

07569 {
07570    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
07571    switch (pos) {
07572    case 4:
07573       if (state == 0) {
07574          return ast_strdup("on");
07575       } else {
07576          return NULL;
07577       }
07578    case 6:
07579       if (state == 0) {
07580          return ast_strdup("in");
07581       } else {
07582          return NULL;
07583       }
07584    case 7:
07585       return complete_queue(line, word, pos, state);
07586    default:
07587       return NULL;
07588    }
07589 }

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

Definition at line 6867 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

06868 {
06869    if (pos == 2)
06870       return complete_queue(line, word, pos, state);
06871    return NULL;
06872 }

static int compress_char ( const char  c  )  [static]

Definition at line 1470 of file app_queue.c.

Referenced by member_hash_fn().

01471 {
01472    if (c < 32)
01473       return 0;
01474    else if (c > 96)
01475       return c - 64;
01476    else
01477       return c - 32;
01478 }

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

Copy rule from global list into specified queue.

Definition at line 5573 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, penalty_rule::list, rule_list::list, 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().

05574 {
05575    struct penalty_rule *pr_iter;
05576    struct rule_list *rl_iter;
05577    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
05578    AST_LIST_LOCK(&rule_lists);
05579    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05580       if (!strcasecmp(rl_iter->name, tmp))
05581          break;
05582    }
05583    if (rl_iter) {
05584       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05585          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05586          if (!new_pr) {
05587             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05588             AST_LIST_UNLOCK(&rule_lists);
05589             break;
05590          }
05591          new_pr->time = pr_iter->time;
05592          new_pr->max_value = pr_iter->max_value;
05593          new_pr->min_value = pr_iter->min_value;
05594          new_pr->max_relative = pr_iter->max_relative;
05595          new_pr->min_relative = pr_iter->min_relative;
05596          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05597       }
05598    }
05599    AST_LIST_UNLOCK(&rule_lists);
05600 }

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

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

Definition at line 1438 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(), LOG_WARNING, member::penalty, S_OR, and strsep().

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

01439 {
01440    struct member *cur;
01441    
01442    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01443       cur->penalty = penalty;
01444       cur->paused = paused;
01445       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01446       if (!ast_strlen_zero(state_interface))
01447          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01448       else
01449          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01450       if (!ast_strlen_zero(membername))
01451          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01452       else
01453          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01454       if (!strchr(cur->interface, '/'))
01455          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01456       if (!strncmp(cur->state_interface, "hint:", 5)) {
01457          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01458          char *exten = strsep(&context, "@") + 5;
01459 
01460          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01461          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01462       }
01463       cur->status = get_queue_member_status(cur);
01464    }
01465 
01466    return cur;
01467 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

01995 {
01996    struct call_queue *q = obj;
01997    int i;
01998 
01999    free_members(q, 1);
02000    ast_string_field_free_memory(q);
02001    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02002       if (q->sound_periodicannounce[i])
02003          free(q->sound_periodicannounce[i]);
02004    }
02005    ao2_ref(q->members, -1);
02006 }

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

Definition at line 1336 of file app_queue.c.

References ast_calloc, ast_device_state(), 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, devicestate_tps, handle_statechange(), LOG_ERROR, and statechange::state.

Referenced by load_module(), and load_pbx().

01337 {
01338    enum ast_device_state state;
01339    const char *device;
01340    struct statechange *sc;
01341    size_t datapsize;
01342 
01343    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01344    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01345 
01346    if (ast_strlen_zero(device)) {
01347       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01348       return;
01349    }
01350    datapsize = sizeof(*sc) + strlen(device) + 1;
01351    if (!(sc = ast_calloc(1, datapsize))) {
01352       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01353       return;
01354    }
01355    sc->state = state;
01356    strcpy(sc->dev, device);
01357    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01358       ast_free(sc);
01359    }
01360 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2780 of file app_queue.c.

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

Referenced by ring_entry().

02781 {
02782    o->stillgoing = 0;
02783    ast_hangup(o->chan);
02784    o->chan = NULL;
02785 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

06704 {
06705    if (s)
06706       astman_append(s, "%s\r\n", str);
06707    else
06708       ast_cli(fd, "%s\n", str);
06709 }

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

04939 {
04940    struct member *cur_member;
04941    char value[PM_MAX_LEN];
04942    int value_len = 0;
04943    int res;
04944    struct ao2_iterator mem_iter;
04945 
04946    memset(value, 0, sizeof(value));
04947 
04948    if (!pm_queue)
04949       return;
04950 
04951    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04952    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04953       if (!cur_member->dynamic) {
04954          ao2_ref(cur_member, -1);
04955          continue;
04956       }
04957 
04958       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04959          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04960 
04961       ao2_ref(cur_member, -1);
04962 
04963       if (res != strlen(value + value_len)) {
04964          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04965          break;
04966       }
04967       value_len += res;
04968    }
04969    ao2_iterator_destroy(&mem_iter);
04970    
04971    if (value_len && !cur_member) {
04972       if (ast_db_put(pm_family, pm_queue->name, value))
04973          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04974    } else
04975       /* Delete the entry if the queue is empty or there is an error */
04976       ast_db_del(pm_family, pm_queue->name);
04977 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 4098 of file app_queue.c.

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

04099 {
04100    struct queue_end_bridge *qeb = data;
04101    struct call_queue *q = qeb->q;
04102    struct ast_channel *chan = qeb->chan;
04103 
04104    if (ao2_ref(qeb, -1) == 1) {
04105       set_queue_variables(q, chan);
04106       /* This unrefs the reference we made in try_calling when we allocated qeb */
04107       queue_t_unref(q, "Expire bridge_config reference");
04108    }
04109 }

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

Definition at line 4091 of file app_queue.c.

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

04092 {
04093    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04094    ao2_ref(qeb, +1);
04095    qeb->chan = originator;
04096 }

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

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

01395 {
01396    struct ao2_iterator miter, qiter;
01397    struct member *m;
01398    struct call_queue *q;
01399    int found = 0, device_state = extensionstate2devicestate(state);
01400 
01401    qiter = ao2_iterator_init(queues, 0);
01402    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01403       ao2_lock(q);
01404 
01405       miter = ao2_iterator_init(q->members, 0);
01406       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01407          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01408             update_status(q, m, device_state);
01409             ao2_ref(m, -1);
01410             found = 1;
01411             break;
01412          }
01413       }
01414       ao2_iterator_destroy(&miter);
01415 
01416       ao2_unlock(q);
01417       queue_t_unref(q, "Done with iterator");
01418    }
01419    ao2_iterator_destroy(&qiter);
01420 
01421         if (found) {
01422       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01423    } else {
01424       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",
01425            exten, context, device_state, ast_devstate2str(device_state));
01426    }
01427 
01428    return 0;
01429 }

static int extensionstate2devicestate ( int  state  )  [static]

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

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

01364 {
01365    switch (state) {
01366    case AST_EXTENSION_NOT_INUSE:
01367       state = AST_DEVICE_NOT_INUSE;
01368       break;
01369    case AST_EXTENSION_INUSE:
01370       state = AST_DEVICE_INUSE;
01371       break;
01372    case AST_EXTENSION_BUSY:
01373       state = AST_DEVICE_BUSY;
01374       break;
01375    case AST_EXTENSION_RINGING:
01376       state = AST_DEVICE_RINGING;
01377       break;
01378    case AST_EXTENSION_ONHOLD:
01379       state = AST_DEVICE_ONHOLD;
01380       break;
01381    case AST_EXTENSION_UNAVAILABLE:
01382       state = AST_DEVICE_UNAVAILABLE;
01383       break;
01384    case AST_EXTENSION_REMOVED:
01385    case AST_EXTENSION_DEACTIVATED:
01386    default:
01387       state = AST_DEVICE_INVALID;
01388       break;
01389    }
01390 
01391    return state;
01392 }

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

find the entry with the best metric, or NULL

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

03015 {
03016    struct callattempt *best = NULL, *cur;
03017 
03018    for (cur = outgoing; cur; cur = cur->q_next) {
03019       if (cur->stillgoing &&              /* Not already done */
03020          !cur->chan &&              /* Isn't already going */
03021          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03022          best = cur;
03023       }
03024    }
03025 
03026    return best;
03027 }

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

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

References alloc_queue(), ao2_lock, ao2_t_find, ao2_unlock, ast_debug, ast_log(), clear_queue(), call_queue::dead, LOG_WARNING, call_queue::membercount, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

02033 {
02034    struct ast_variable *v;
02035    struct call_queue *q, tmpq = {
02036       .name = queuename,   
02037    };
02038    struct member *m;
02039    struct ao2_iterator mem_iter;
02040    char *interface = NULL;
02041    const char *tmp_name;
02042    char *tmp;
02043    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
02044 
02045    /* Static queues override realtime. */
02046    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02047       ao2_lock(q);
02048       if (!q->realtime) {
02049          if (q->dead) {
02050             ao2_unlock(q);
02051             queue_t_unref(q, "Queue is dead; can't return it");
02052             return NULL;
02053          } else {
02054             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02055             ao2_unlock(q);
02056             return q;
02057          }
02058       }
02059    } else if (!member_config)
02060       /* Not found in the list, and it's not realtime ... */
02061       return NULL;
02062 
02063    /* Check if queue is defined in realtime. */
02064    if (!queue_vars) {
02065       /* Delete queue from in-core list if it has been deleted in realtime. */
02066       if (q) {
02067          /*! \note Hmm, can't seem to distinguish a DB failure from a not
02068             found condition... So we might delete an in-core queue
02069             in case of DB failure. */
02070          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02071 
02072          q->dead = 1;
02073          /* Delete if unused (else will be deleted when last caller leaves). */
02074          queues_t_unlink(queues, q, "Unused; removing from container");
02075          ao2_unlock(q);
02076          queue_t_unref(q, "Queue is dead; can't return it");
02077       }
02078       return NULL;
02079    }
02080 
02081    /* Create a new queue if an in-core entry does not exist yet. */
02082    if (!q) {
02083       struct ast_variable *tmpvar = NULL;
02084       if (!(q = alloc_queue(queuename)))
02085          return NULL;
02086       ao2_lock(q);
02087       clear_queue(q);
02088       q->realtime = 1;
02089       q->membercount = 0;
02090       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
02091        * will allocate the members properly
02092        */
02093       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02094          if (!strcasecmp(tmpvar->name, "strategy")) {
02095             q->strategy = strat2int(tmpvar->value);
02096             if (q->strategy < 0) {
02097                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02098                tmpvar->value, q->name);
02099                q->strategy = QUEUE_STRATEGY_RINGALL;
02100             }
02101             break;
02102          }
02103       }
02104       /* We traversed all variables and didn't find a strategy */
02105       if (!tmpvar)
02106          q->strategy = QUEUE_STRATEGY_RINGALL;
02107       queues_t_link(queues, q, "Add queue to container");
02108    }
02109    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
02110 
02111    memset(tmpbuf, 0, sizeof(tmpbuf));
02112    for (v = queue_vars; v; v = v->next) {
02113       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
02114       if ((tmp = strchr(v->name, '_'))) {
02115          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02116          tmp_name = tmpbuf;
02117          tmp = tmpbuf;
02118          while ((tmp = strchr(tmp, '_')))
02119             *tmp++ = '-';
02120       } else
02121          tmp_name = v->name;
02122 
02123       /* NULL values don't get returned from realtime; blank values should
02124        * still get set.  If someone doesn't want a value to be set, they
02125        * should set the realtime column to NULL, not blank. */
02126       queue_set_param(q, tmp_name, v->value, -1, 0);
02127    }
02128 
02129    /* Temporarily set realtime members dead so we can detect deleted ones. 
02130     * Also set the membercount correctly for realtime*/
02131    mem_iter = ao2_iterator_init(q->members, 0);
02132    while ((m = ao2_iterator_next(&mem_iter))) {
02133       q->membercount++;
02134       if (m->realtime)
02135          m->dead = 1;
02136       ao2_ref(m, -1);
02137    }
02138    ao2_iterator_destroy(&mem_iter);
02139 
02140    while ((interface = ast_category_browse(member_config, interface))) {
02141       rt_handle_member_record(q, interface,
02142          ast_variable_retrieve(member_config, interface, "uniqueid"),
02143          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02144          ast_variable_retrieve(member_config, interface, "penalty"),
02145          ast_variable_retrieve(member_config, interface, "paused"),
02146          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02147    }
02148 
02149    /* Delete all realtime members that have been deleted in DB. */
02150    mem_iter = ao2_iterator_init(q->members, 0);
02151    while ((m = ao2_iterator_next(&mem_iter))) {
02152       if (m->dead) {
02153          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02154          ao2_unlink(q->members, m);
02155          q->membercount--;
02156       }
02157       ao2_ref(m, -1);
02158    }
02159    ao2_iterator_destroy(&mem_iter);
02160 
02161    ao2_unlock(q);
02162 
02163    return q;
02164 }

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

Iterate through queue's member list and delete them.

Definition at line 1977 of file app_queue.c.

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

Referenced by destroy_queue().

01978 {
01979    /* Free non-dynamic members */
01980    struct member *cur;
01981    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01982 
01983    while ((cur = ao2_iterator_next(&mem_iter))) {
01984       if (all || !cur->dynamic) {
01985          ao2_unlink(q->members, cur);
01986          q->membercount--;
01987       }
01988       ao2_ref(cur, -1);
01989    }
01990    ao2_iterator_destroy(&mem_iter);
01991 }

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

Definition at line 5218 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, and RESULT_FAILURE.

Referenced by queue_function_memberpenalty_read().

05219 {
05220    int foundqueue = 0, penalty;
05221    struct call_queue *q, tmpq = {
05222       .name = queuename,   
05223    };
05224    struct member *mem;
05225    
05226    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05227       foundqueue = 1;
05228       ao2_lock(q);
05229       if ((mem = interface_exists(q, interface))) {
05230          penalty = mem->penalty;
05231          ao2_ref(mem, -1);
05232          ao2_unlock(q);
05233          queue_t_unref(q, "Search complete");
05234          return penalty;
05235       }
05236       ao2_unlock(q);
05237       queue_t_unref(q, "Search complete");
05238    }
05239 
05240    /* some useful debuging */
05241    if (foundqueue) 
05242       ast_log (LOG_ERROR, "Invalid queuename\n");
05243    else 
05244       ast_log (LOG_ERROR, "Invalid interface\n");
05245 
05246    return RESULT_FAILURE;
05247 }

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

01188 {
01189    struct member *member;
01190    struct ao2_iterator mem_iter;
01191 
01192    ao2_lock(q);
01193    mem_iter = ao2_iterator_init(q->members, 0);
01194    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01195       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
01196          if (conditions & QUEUE_EMPTY_PENALTY) {
01197             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01198             continue;
01199          }
01200       }
01201 
01202       switch (member->status) {
01203       case AST_DEVICE_INVALID:
01204          if (conditions & QUEUE_EMPTY_INVALID) {
01205             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01206             break;
01207          }
01208          goto default_case;
01209       case AST_DEVICE_UNAVAILABLE:
01210          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01211             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01212             break;
01213          }
01214          goto default_case;
01215       case AST_DEVICE_INUSE:
01216          if (conditions & QUEUE_EMPTY_INUSE) {
01217             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01218             break;
01219          }
01220          goto default_case;
01221       case AST_DEVICE_RINGING:
01222          if (conditions & QUEUE_EMPTY_RINGING) {
01223             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01224             break;
01225          }
01226       case AST_DEVICE_UNKNOWN:
01227          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01228             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01229             break;
01230          }
01231          /* Fall-through */
01232       default:
01233       default_case:
01234          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01235             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01236             break;
01237          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01238             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);
01239             break;
01240          } else {
01241             ao2_unlock(q);
01242             ao2_ref(member, -1);
01243             ao2_iterator_destroy(&mem_iter);
01244             ast_debug(4, "%s is available.\n", member->membername);
01245             return 0;
01246          }
01247          break;
01248       }
01249    }
01250    ao2_iterator_destroy(&mem_iter);
01251 
01252    ao2_unlock(q);
01253    return -1;
01254 }

static int get_queue_member_status ( struct member cur  )  [static]

Return the current state of a member.

Definition at line 1432 of file app_queue.c.

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

01433 {
01434    return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
01435 }

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

Definition at line 7333 of file app_queue.c.

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

07334 {
07335    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07336    int penalty;
07337 
07338    switch ( cmd ) {
07339    case CLI_INIT:
07340       e->command = "queue add member";
07341       e->usage =
07342          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07343          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
07344       return NULL;
07345    case CLI_GENERATE:
07346       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07347    }
07348 
07349    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07350       return CLI_SHOWUSAGE;
07351    } else if (strcmp(a->argv[4], "to")) {
07352       return CLI_SHOWUSAGE;
07353    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07354       return CLI_SHOWUSAGE;
07355    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07356       return CLI_SHOWUSAGE;
07357    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07358       return CLI_SHOWUSAGE;
07359    }
07360 
07361    queuename = a->argv[5];
07362    interface = a->argv[3];
07363    if (a->argc >= 8) {
07364       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07365          if (penalty < 0) {
07366             ast_cli(a->fd, "Penalty must be >= 0\n");
07367             penalty = 0;
07368          }
07369       } else {
07370          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07371          penalty = 0;
07372       }
07373    } else {
07374       penalty = 0;
07375    }
07376 
07377    if (a->argc >= 10) {
07378       membername = a->argv[9];
07379    }
07380 
07381    if (a->argc >= 12) {
07382       state_interface = a->argv[11];
07383    }
07384 
07385    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07386    case RES_OKAY:
07387       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
07388       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
07389       return CLI_SUCCESS;
07390    case RES_EXISTS:
07391       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
07392       return CLI_FAILURE;
07393    case RES_NOSUCHQUEUE:
07394       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
07395       return CLI_FAILURE;
07396    case RES_OUTOFMEMORY:
07397       ast_cli(a->fd, "Out of memory\n");
07398       return CLI_FAILURE;
07399    case RES_NOT_DYNAMIC:
07400       ast_cli(a->fd, "Member not dynamic\n");
07401       return CLI_FAILURE;
07402    default:
07403       return CLI_FAILURE;
07404    }
07405 }

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

Definition at line 7517 of file app_queue.c.

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

07518 {
07519    const char *queuename, *interface, *reason;
07520    int paused;
07521 
07522    switch (cmd) {
07523    case CLI_INIT:
07524       e->command = "queue {pause|unpause} member";
07525       e->usage = 
07526          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
07527          "  Pause or unpause a queue member. Not specifying a particular queue\n"
07528          "  will pause or unpause a member across all queues to which the member\n"
07529          "  belongs.\n";
07530       return NULL;
07531    case CLI_GENERATE:
07532       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
07533    }
07534 
07535    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
07536       return CLI_SHOWUSAGE;
07537    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
07538       return CLI_SHOWUSAGE;
07539    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
07540       return CLI_SHOWUSAGE;
07541    }
07542 
07543 
07544    interface = a->argv[3];
07545    queuename = a->argc >= 6 ? a->argv[5] : NULL;
07546    reason = a->argc == 8 ? a->argv[7] : NULL;
07547    paused = !strcasecmp(a->argv[1], "pause");
07548 
07549    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
07550       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
07551       if (!ast_strlen_zero(queuename))
07552          ast_cli(a->fd, " in queue '%s'", queuename);
07553       if (!ast_strlen_zero(reason))
07554          ast_cli(a->fd, " for reason '%s'", reason);
07555       ast_cli(a->fd, "\n");
07556       return CLI_SUCCESS;
07557    } else {
07558       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
07559       if (!ast_strlen_zero(queuename))
07560          ast_cli(a->fd, " in queue '%s'", queuename);
07561       if (!ast_strlen_zero(reason))
07562          ast_cli(a->fd, " for reason '%s'", reason);
07563       ast_cli(a->fd, "\n");
07564       return CLI_FAILURE;
07565    }
07566 }

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

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

07727 {
07728    struct ast_flags mask = {0,};
07729    int i;
07730 
07731    switch (cmd) {
07732       case CLI_INIT:
07733          e->command = "queue reload {parameters|members|rules|all}";
07734          e->usage =
07735             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
07736             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
07737             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
07738             "specified in order to know what information to reload. Below is an explanation\n"
07739             "of each of these qualifiers.\n"
07740             "\n"
07741             "\t'members' - reload queue members from queues.conf\n"
07742             "\t'parameters' - reload all queue options except for queue members\n"
07743             "\t'rules' - reload the queuerules.conf file\n"
07744             "\t'all' - reload queue rules, parameters, and members\n"
07745             "\n"
07746             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
07747             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
07748             "one queue is specified when using this command, reloading queue rules may cause\n"
07749             "other queues to be affected\n";
07750          return NULL;
07751       case CLI_GENERATE:
07752          if (a->pos >= 3) {
07753             return complete_queue(a->line, a->word, a->pos, a->n);
07754          } else {
07755             return NULL;
07756          }
07757    }
07758 
07759    if (a->argc < 3)
07760       return CLI_SHOWUSAGE;
07761 
07762    if (!strcasecmp(a->argv[2], "rules")) {
07763       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07764    } else if (!strcasecmp(a->argv[2], "members")) {
07765       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07766    } else if (!strcasecmp(a->argv[2], "parameters")) {
07767       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07768    } else if (!strcasecmp(a->argv[2], "all")) {
07769       ast_set_flag(&mask, AST_FLAGS_ALL);
07770    }
07771 
07772    if (a->argc == 3) {
07773       reload_handler(1, &mask, NULL);
07774       return CLI_SUCCESS;
07775    }
07776 
07777    for (i = 3; i < a->argc; ++i) {
07778       reload_handler(1, &mask, a->argv[i]);
07779    }
07780 
07781    return CLI_SUCCESS;
07782 }

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

Definition at line 7452 of file app_queue.c.

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

07453 {
07454    const char *queuename, *interface;
07455 
07456    switch (cmd) {
07457    case CLI_INIT:
07458       e->command = "queue remove member";
07459       e->usage = 
07460          "Usage: queue remove member <channel> from <queue>\n"
07461          "       Remove a specific channel from a queue.\n";
07462       return NULL;
07463    case CLI_GENERATE:
07464       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
07465    }
07466 
07467    if (a->argc != 6) {
07468       return CLI_SHOWUSAGE;
07469    } else if (strcmp(a->argv[4], "from")) {
07470       return CLI_SHOWUSAGE;
07471    }
07472 
07473    queuename = a->argv[5];
07474    interface = a->argv[3];
07475 
07476    switch (remove_from_queue(queuename, interface)) {
07477    case RES_OKAY:
07478       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
07479       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
07480       return CLI_SUCCESS;
07481    case RES_EXISTS:
07482       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
07483       return CLI_FAILURE;
07484    case RES_NOSUCHQUEUE:
07485       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
07486       return CLI_FAILURE;
07487    case RES_OUTOFMEMORY:
07488       ast_cli(a->fd, "Out of memory\n");
07489       return CLI_FAILURE;
07490    case RES_NOT_DYNAMIC:
07491       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
07492       return CLI_FAILURE;
07493    default:
07494       return CLI_FAILURE;
07495    }
07496 }

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

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

07688 {
07689    struct ast_flags mask = {QUEUE_RESET_STATS,};
07690    int i;
07691 
07692    switch (cmd) {
07693       case CLI_INIT:
07694          e->command = "queue reset stats";
07695          e->usage =
07696             "Usage: queue reset stats [<queuenames>]\n"
07697             "\n"
07698             "Issuing this command will reset statistics for\n"
07699             "<queuenames>, or for all queues if no queue is\n"
07700             "specified.\n";
07701          return NULL;
07702       case CLI_GENERATE:
07703          if (a->pos >= 3) {
07704             return complete_queue(a->line, a->word, a->pos, a->n);
07705          } else {
07706             return NULL;
07707          }
07708    }
07709 
07710    if (a->argc < 3) {
07711       return CLI_SHOWUSAGE;
07712    }
07713 
07714    if (a->argc == 3) {
07715       reload_handler(1, &mask, NULL);
07716       return CLI_SUCCESS;
07717    }
07718 
07719    for (i = 3; i < a->argc; ++i) {
07720       reload_handler(1, &mask, a->argv[i]);
07721    }
07722 
07723    return CLI_SUCCESS;
07724 }

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

Definition at line 7653 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::list, 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.

07654 {
07655    const char *rule;
07656    struct rule_list *rl_iter;
07657    struct penalty_rule *pr_iter;
07658    switch (cmd) {
07659    case CLI_INIT:
07660       e->command = "queue show rules";
07661       e->usage =
07662       "Usage: queue show rules [rulename]\n"
07663       "  Show the list of rules associated with rulename. If no\n"
07664       "  rulename is specified, list all rules defined in queuerules.conf\n";
07665       return NULL;
07666    case CLI_GENERATE:
07667       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
07668    }
07669 
07670    if (a->argc != 3 && a->argc != 4)
07671       return CLI_SHOWUSAGE;
07672 
07673    rule = a->argc == 4 ? a->argv[3] : "";
07674    AST_LIST_LOCK(&rule_lists);
07675    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07676       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
07677          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
07678          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07679             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);
07680          }
07681       }
07682    }
07683    AST_LIST_UNLOCK(&rule_lists);
07684    return CLI_SUCCESS; 
07685 }

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

Definition at line 7591 of file app_queue.c.

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

07592 {
07593    const char *queuename = NULL, *interface;
07594    int penalty = 0;
07595 
07596    switch (cmd) {
07597    case CLI_INIT:
07598       e->command = "queue set penalty";
07599       e->usage = 
07600       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
07601       "  Set a member's penalty in the queue specified. If no queue is specified\n"
07602       "  then that interface's penalty is set in all queues to which that interface is a member\n";
07603       return NULL;
07604    case CLI_GENERATE:
07605       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
07606    }
07607 
07608    if (a->argc != 6 && a->argc != 8) {
07609       return CLI_SHOWUSAGE;
07610    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
07611       return CLI_SHOWUSAGE;
07612    }
07613 
07614    if (a->argc == 8)
07615       queuename = a->argv[7];
07616    interface = a->argv[5];
07617    penalty = atoi(a->argv[3]);
07618 
07619    switch (set_member_penalty(queuename, interface, penalty)) {
07620    case RESULT_SUCCESS:
07621       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07622       return CLI_SUCCESS;
07623    case RESULT_FAILURE:
07624       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07625       return CLI_FAILURE;
07626    default:
07627       return CLI_FAILURE;
07628    }
07629 }

static int handle_statechange ( void *  datap  )  [static]

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

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

01293 {
01294    struct statechange *sc = datap;
01295    struct ao2_iterator miter, qiter;
01296    struct member *m;
01297    struct call_queue *q;
01298    char interface[80], *slash_pos;
01299    int found = 0;
01300 
01301    qiter = ao2_iterator_init(queues, 0);
01302    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01303       ao2_lock(q);
01304 
01305       miter = ao2_iterator_init(q->members, 0);
01306       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01307          ast_copy_string(interface, m->state_interface, sizeof(interface));
01308 
01309          if ((slash_pos = strchr(interface, '/')))
01310             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01311                *slash_pos = '\0';
01312 
01313          if (!strcasecmp(interface, sc->dev)) {
01314             found = 1;
01315             update_status(q, m, sc->state);
01316             ao2_ref(m, -1);
01317             break;
01318          }
01319       }
01320       ao2_iterator_destroy(&miter);
01321 
01322       ao2_unlock(q);
01323       queue_t_unref(q, "Done with iterator");
01324    }
01325    ao2_iterator_destroy(&qiter);
01326 
01327    if (found)
01328       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01329    else
01330       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));
01331 
01332    ast_free(sc);
01333    return 0;
01334 }

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

02674 {
02675    struct callattempt *oo;
02676 
02677    while (outgoing) {
02678       /* If someone else answered the call we should indicate this in the CANCEL */
02679       /* Hangup any existing lines we have open */
02680       if (outgoing->chan && (outgoing->chan != exception)) {
02681          if (exception || cancel_answered_elsewhere)
02682             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02683          ast_hangup(outgoing->chan);
02684       }
02685       oo = outgoing;
02686       outgoing = outgoing->q_next;
02687       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
02688       callattempt_free(oo);
02689    }
02690 }

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

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

01503 {
01504    int i;
01505    struct penalty_rule *pr_iter;
01506 
01507    q->dead = 0;
01508    q->retry = DEFAULT_RETRY;
01509    q->timeout = DEFAULT_TIMEOUT;
01510    q->maxlen = 0;
01511    q->announcefrequency = 0;
01512    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01513    q->announceholdtime = 1;
01514    q->announcepositionlimit = 10; /* Default 10 positions */
01515    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01516    q->roundingseconds = 0; /* Default - don't announce seconds */
01517    q->servicelevel = 0;
01518    q->ringinuse = 1;
01519    q->setinterfacevar = 0;
01520    q->setqueuevar = 0;
01521    q->setqueueentryvar = 0;
01522    q->autofill = autofill_default;
01523    q->montype = montype_default;
01524    q->monfmt[0] = '\0';
01525    q->reportholdtime = 0;
01526    q->wrapuptime = 0;
01527    q->penaltymemberslimit = 0;
01528    q->joinempty = 0;
01529    q->leavewhenempty = 0;
01530    q->memberdelay = 0;
01531    q->maskmemberstatus = 0;
01532    q->eventwhencalled = 0;
01533    q->weight = 0;
01534    q->timeoutrestart = 0;
01535    q->periodicannouncefrequency = 0;
01536    q->randomperiodicannounce = 0;
01537    q->numperiodicannounce = 0;
01538    q->autopause = QUEUE_AUTOPAUSE_OFF;
01539    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01540    if (!q->members) {
01541       if (q->strategy == QUEUE_STRATEGY_LINEAR)
01542          /* linear strategy depends on order, so we have to place all members in a single bucket */
01543          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01544       else
01545          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01546    }
01547    q->found = 1;
01548 
01549    ast_string_field_set(q, sound_next, "queue-youarenext");
01550    ast_string_field_set(q, sound_thereare, "queue-thereare");
01551    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01552    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01553    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01554    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01555    ast_string_field_set(q, sound_minutes, "queue-minutes");
01556    ast_string_field_set(q, sound_minute, "queue-minute");
01557    ast_string_field_set(q, sound_seconds, "queue-seconds");
01558    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01559    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01560 
01561    if ((q->sound_periodicannounce[0] = ast_str_create(32)))
01562       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01563 
01564    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01565       if (q->sound_periodicannounce[i])
01566          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01567    }
01568 
01569    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01570       ast_free(pr_iter);
01571 }

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

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

Referenced by join_queue().

01158 {
01159    struct queue_ent *cur;
01160 
01161    if (!q || !new)
01162       return;
01163    if (prev) {
01164       cur = prev->next;
01165       prev->next = new;
01166    } else {
01167       cur = q->head;
01168       q->head = new;
01169    }
01170    new->next = cur;
01171 
01172    /* every queue_ent must have a reference to it's parent call_queue, this
01173     * reference does not go away until the end of the queue_ent's life, meaning
01174     * that even when the queue_ent leaves the call_queue this ref must remain. */
01175    queue_ref(q);
01176    new->parent = q;
01177    new->pos = ++(*pos);
01178    new->opos = *pos;
01179 }

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 1602 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(), rule_list::list, LOG_WARNING, rule_list::name, and rule_list::rules.

Referenced by reload_queue_rules().

01603 {
01604    char *timestr, *maxstr, *minstr, *contentdup;
01605    struct penalty_rule *rule = NULL, *rule_iter;
01606    struct rule_list *rl_iter;
01607    int penaltychangetime, inserted = 0;
01608 
01609    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01610       return -1;
01611    }
01612 
01613    contentdup = ast_strdupa(content);
01614    
01615    if (!(maxstr = strchr(contentdup, ','))) {
01616       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01617       ast_free(rule);
01618       return -1;
01619    }
01620 
01621    *maxstr++ = '\0';
01622    timestr = contentdup;
01623 
01624    if ((penaltychangetime = atoi(timestr)) < 0) {
01625       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01626       ast_free(rule);
01627       return -1;
01628    }
01629 
01630    rule->time = penaltychangetime;
01631 
01632    if ((minstr = strchr(maxstr,',')))
01633       *minstr++ = '\0';
01634    
01635    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01636     * OR if a min penalty change is indicated but no max penalty change is */
01637    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01638       rule->max_relative = 1;
01639    }
01640 
01641    rule->max_value = atoi(maxstr);
01642 
01643    if (!ast_strlen_zero(minstr)) {
01644       if (*minstr == '+' || *minstr == '-')
01645          rule->min_relative = 1;
01646       rule->min_value = atoi(minstr);
01647    } else /*there was no minimum specified, so assume this means no change*/
01648       rule->min_relative = 1;
01649 
01650    /*We have the rule made, now we need to insert it where it belongs*/
01651    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01652       if (strcasecmp(rl_iter->name, list_name))
01653          continue;
01654 
01655       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01656          if (rule->time < rule_iter->time) {
01657             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01658             inserted = 1;
01659             break;
01660          }
01661       }
01662       AST_LIST_TRAVERSE_SAFE_END;
01663    
01664       if (!inserted) {
01665          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01666       }
01667    }
01668 
01669    return 0;
01670 }

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

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

01050 {
01051    int x;
01052 
01053    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01054       if (strategy == strategies[x].strategy)
01055          return strategies[x].name;
01056    }
01057 
01058    return "<unknown>";
01059 }

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

Definition at line 4912 of file app_queue.c.

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

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

04913 {
04914    struct member *mem;
04915    struct ao2_iterator mem_iter;
04916 
04917    if (!q)
04918       return NULL;
04919 
04920    mem_iter = ao2_iterator_init(q->members, 0);
04921    while ((mem = ao2_iterator_next(&mem_iter))) {
04922       if (!strcasecmp(interface, mem->interface)) {
04923          ao2_iterator_destroy(&mem_iter);
04924          return mem;
04925       }
04926       ao2_ref(mem, -1);
04927    }
04928    ao2_iterator_destroy(&mem_iter);
04929 
04930    return NULL;
04931 }

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

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

Referenced by queue_exec(), and wait_our_turn().

03682 {
03683    struct queue_ent *ch;
03684    int res;
03685    int avl;
03686    int idx = 0;
03687    /* This needs a lock. How many members are available to be served? */
03688    ao2_lock(qe->parent);
03689 
03690    avl = num_available_members(qe->parent);
03691 
03692    ch = qe->parent->head;
03693 
03694    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03695 
03696    while ((idx < avl) && (ch) && (ch != qe)) {
03697       if (!ch->pending)
03698          idx++;
03699       ch = ch->next;       
03700    }
03701 
03702    ao2_unlock(qe->parent);
03703    /* If the queue entry is within avl [the number of available members] calls from the top ... 
03704     * Autofill and position check added to support autofill=no (as only calls
03705     * from the front of the queue are valid when autofill is disabled)
03706     */
03707    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03708       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
03709       res = 1;
03710    } else {
03711       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
03712       res = 0;
03713    }
03714 
03715    return res;
03716 }

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

Definition at line 2292 of file app_queue.c.

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

Referenced by queue_exec().

02293 {
02294    struct call_queue *q;
02295    struct queue_ent *cur, *prev = NULL;
02296    int res = -1;
02297    int pos = 0;
02298    int inserted = 0;
02299 
02300    if (!(q = load_realtime_queue(queuename)))
02301       return res;
02302 
02303    ao2_lock(queues);
02304    ao2_lock(q);
02305 
02306    /* This is our one */
02307    if (q->joinempty) {
02308       int status = 0;
02309       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
02310          *reason = QUEUE_JOINEMPTY;
02311          ao2_unlock(q);
02312          ao2_unlock(queues);
02313          return res;
02314       }
02315    }
02316    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02317       *reason = QUEUE_FULL;
02318    else if (*reason == QUEUE_UNKNOWN) {
02319       /* There's space for us, put us at the right position inside
02320        * the queue.
02321        * Take into account the priority of the calling user */
02322       inserted = 0;
02323       prev = NULL;
02324       cur = q->head;
02325       while (cur) {
02326          /* We have higher priority than the current user, enter
02327           * before him, after all the other users with priority
02328           * higher or equal to our priority. */
02329          if ((!inserted) && (qe->prio > cur->prio)) {
02330             insert_entry(q, prev, qe, &pos);
02331             inserted = 1;
02332          }
02333          /* <= is necessary for the position comparison because it may not be possible to enter
02334           * at our desired position since higher-priority callers may have taken the position we want
02335           */
02336          if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02337             insert_entry(q, prev, qe, &pos);
02338             /*pos is incremented inside insert_entry, so don't need to add 1 here*/
02339             if (position < pos) {
02340                ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02341             }
02342             inserted = 1;
02343          }
02344          cur->pos = ++pos;
02345          prev = cur;
02346          cur = cur->next;
02347       }
02348       /* No luck, join at the end of the queue */
02349       if (!inserted)
02350          insert_entry(q, prev, qe, &pos);
02351       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02352       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02353       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02354       q->count++;
02355       res = 0;
02356       ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02357          "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
02358          qe->chan->name,
02359          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02360          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02361          q->name, qe->pos, q->count, qe->chan->uniqueid );
02362       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02363    }
02364    ao2_unlock(q);
02365    ao2_unlock(queues);
02366 
02367    return res;
02368 }

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

Definition at line 6437 of file app_queue.c.

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

06438 {
06439    struct member *member = obj;
06440    struct call_queue *q = arg;
06441 
06442    if (!member->delme) {
06443       if (member->dynamic) {
06444          /* dynamic members were not counted toward the member count
06445           * when reloading members from queues.conf, so we do that here
06446           */
06447          q->membercount++;
06448       }
06449       member->status = get_queue_member_status(member);
06450       return 0;
06451    } else {
06452       q->membercount--;
06453       return CMP_MATCH;
06454    }
06455 }

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

Definition at line 6575 of file app_queue.c.

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

Referenced by reload_queues().

06576 {
06577    struct call_queue *q = obj;
06578    char *queuename = arg;
06579    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
06580       return CMP_MATCH;
06581    } else {
06582       return 0;
06583    }
06584 }

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

References ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_manager_event, ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, penalty_rule::list, call_queue::name, ast_channel::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, ast_channel::uniqueid, and var.

Referenced by wait_our_turn().

02596 {
02597    struct call_queue *q;
02598    struct queue_ent *current, *prev = NULL;
02599    struct penalty_rule *pr_iter;
02600    int pos = 0;
02601 
02602    if (!(q = qe->parent))
02603       return;
02604    queue_t_ref(q, "Copy queue pointer from queue entry");
02605    ao2_lock(q);
02606 
02607    prev = NULL;
02608    for (current = q->head; current; current = current->next) {
02609       if (current == qe) {
02610          char posstr[20];
02611          q->count--;
02612 
02613          /* Take us out of the queue */
02614          ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
02615             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
02616             qe->chan->name, q->name,  q->count, qe->pos, qe->chan->uniqueid);
02617          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02618          /* Take us out of the queue */
02619          if (prev)
02620             prev->next = current->next;
02621          else
02622             q->head = current->next;
02623          /* Free penalty rules */
02624          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02625             ast_free(pr_iter);
02626          snprintf(posstr, sizeof(posstr), "%d", qe->pos);
02627          pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
02628       } else {
02629          /* Renumber the people after us in the queue based on a new count */
02630          current->pos = ++pos;
02631          prev = current;
02632       }
02633    }
02634    ao2_unlock(q);
02635 
02636    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02637    if (q->realtime) {
02638       struct ast_variable *var;
02639       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02640          q->dead = 1;
02641       } else {
02642          ast_variables_destroy(var);
02643       }
02644    }
02645 
02646    if (q->dead) { 
02647       /* It's dead and nobody is in it, so kill it */
02648       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02649    }
02650    /* unref the explicit ref earlier in the function */
02651    queue_t_unref(q, "Expire copied reference");
02652 }

static int load_module ( void   )  [static]

Definition at line 8125 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(), device_state_sub, devicestate_tps, 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, queuevar_function, queuewaitingcount_function, reload_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().

08126 {
08127    int res;
08128    struct ast_context *con;
08129    struct ast_flags mask = {AST_FLAGS_ALL, };
08130 
08131    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08132 
08133    use_weight = 0;
08134 
08135    if (reload_handler(0, &mask, NULL))
08136       return AST_MODULE_LOAD_DECLINE;
08137 
08138    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08139    if (!con)
08140       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08141    else
08142       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08143 
08144    if (queue_persistent_members)
08145       reload_queue_members();
08146 
08147    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08148 
08149    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08150    res = ast_register_application_xml(app, queue_exec);
08151    res |= ast_register_application_xml(app_aqm, aqm_exec);
08152    res |= ast_register_application_xml(app_rqm, rqm_exec);
08153    res |= ast_register_application_xml(app_pqm, pqm_exec);
08154    res |= ast_register_application_xml(app_upqm, upqm_exec);
08155    res |= ast_register_application_xml(app_ql, ql_exec);
08156    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08157    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08158    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08159    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08160    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08161    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08162    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08163    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08164    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08165    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08166    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08167    res |= ast_custom_function_register(&queuevar_function);
08168    res |= ast_custom_function_register(&queueexists_function);
08169    res |= ast_custom_function_register(&queuemembercount_function);
08170    res |= ast_custom_function_register(&queuemembercount_dep);
08171    res |= ast_custom_function_register(&queuememberlist_function);
08172    res |= ast_custom_function_register(&queuewaitingcount_function);
08173    res |= ast_custom_function_register(&queuememberpenalty_function);
08174 
08175    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08176       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08177    }
08178 
08179    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08180    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08181       res = -1;
08182    }
08183 
08184    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08185 
08186    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08187 
08188    return res ? AST_MODULE_LOAD_DECLINE : 0;
08189 }

static struct call_queue* load_realtime_queue ( const char *  queuename  )  [static]

Definition at line 2166 of file app_queue.c.

References ao2_lock, ao2_t_find, ao2_unlock, ast_atomic_fetchadd_int(), ast_config_destroy(), ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, OBJ_POINTER, queues, call_queue::realtime, SENTINEL, update_realtime_members(), and call_queue::weight.

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

02167 {
02168    struct ast_variable *queue_vars;
02169    struct ast_config *member_config = NULL;
02170    struct call_queue *q = NULL, tmpq = {
02171       .name = queuename,   
02172    };
02173    int prev_weight = 0;
02174 
02175    /* Find the queue in the in-core list first. */
02176    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02177 
02178    if (!q || q->realtime) {
02179       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
02180          queue operations while waiting for the DB.
02181 
02182          This will be two separate database transactions, so we might
02183          see queue parameters as they were before another process
02184          changed the queue and member list as it was after the change.
02185          Thus we might see an empty member list when a queue is
02186          deleted. In practise, this is unlikely to cause a problem. */
02187 
02188       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02189       if (queue_vars) {
02190          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02191          if (!member_config) {
02192             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
02193             ast_variables_destroy(queue_vars);
02194             return NULL;
02195          }
02196       }
02197       if (q) {
02198          prev_weight = q->weight ? 1 : 0;
02199       }
02200 
02201       ao2_lock(queues);
02202 
02203       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02204       if (member_config) {
02205          ast_config_destroy(member_config);
02206       }
02207       if (queue_vars) {
02208          ast_variables_destroy(queue_vars);
02209       }
02210       /* update the use_weight value if the queue's has gained or lost a weight */ 
02211       if (q) {
02212          if (!q->weight && prev_weight) {
02213             ast_atomic_fetchadd_int(&use_weight, -1);
02214          }
02215          if (q->weight && !prev_weight) {
02216             ast_atomic_fetchadd_int(&use_weight, +1);
02217          }
02218       }
02219       /* Other cases will end up with the proper value for use_weight */
02220       ao2_unlock(queues);
02221 
02222    } else {
02223       update_realtime_members(q);
02224    }
02225    return q;
02226 }

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

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

07101 {
07102    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07103    int paused, penalty = 0;
07104 
07105    queuename = astman_get_header(m, "Queue");
07106    interface = astman_get_header(m, "Interface");
07107    penalty_s = astman_get_header(m, "Penalty");
07108    paused_s = astman_get_header(m, "Paused");
07109    membername = astman_get_header(m, "MemberName");
07110    state_interface = astman_get_header(m, "StateInterface");
07111 
07112    if (ast_strlen_zero(queuename)) {
07113       astman_send_error(s, m, "'Queue' not specified.");
07114       return 0;
07115    }
07116 
07117    if (ast_strlen_zero(interface)) {
07118       astman_send_error(s, m, "'Interface' not specified.");
07119       return 0;
07120    }
07121 
07122    if (ast_strlen_zero(penalty_s))
07123       penalty = 0;
07124    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07125       penalty = 0;
07126 
07127    if (ast_strlen_zero(paused_s))
07128       paused = 0;
07129    else
07130       paused = abs(ast_true(paused_s));
07131 
07132    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07133    case RES_OKAY:
07134       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
07135       astman_send_ack(s, m, "Added interface to queue");
07136       break;
07137    case RES_EXISTS:
07138       astman_send_error(s, m, "Unable to add interface: Already there");
07139       break;
07140    case RES_NOSUCHQUEUE:
07141       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07142       break;
07143    case RES_OUTOFMEMORY:
07144       astman_send_error(s, m, "Out of memory");
07145       break;
07146    }
07147 
07148    return 0;
07149 }

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

Definition at line 7185 of file app_queue.c.

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

Referenced by load_module().

07186 {
07187    const char *queuename, *interface, *paused_s, *reason;
07188    int paused;
07189 
07190    interface = astman_get_header(m, "Interface");
07191    paused_s = astman_get_header(m, "Paused");
07192    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07193    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07194 
07195    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07196       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07197       return 0;
07198    }
07199 
07200    paused = abs(ast_true(paused_s));
07201 
07202    if (set_member_paused(queuename, interface, reason, paused))
07203       astman_send_error(s, m, "Interface not found");
07204    else
07205       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07206    return 0;
07207 }

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

Definition at line 7209 of file app_queue.c.

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

Referenced by load_module().

07210 {
07211    const char *queuename, *event, *message, *interface, *uniqueid;
07212 
07213    queuename = astman_get_header(m, "Queue");
07214    uniqueid = astman_get_header(m, "UniqueId");
07215    interface = astman_get_header(m, "Interface");
07216    event = astman_get_header(m, "Event");
07217    message = astman_get_header(m, "Message");
07218 
07219    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07220       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07221       return 0;
07222    }
07223 
07224    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07225    astman_send_ack(s, m, "Event added successfully");
07226 
07227    return 0;
07228 }

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

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

07309 {
07310    const char *queuename, *interface, *penalty_s;
07311    int penalty;
07312 
07313    interface = astman_get_header(m, "Interface");
07314    penalty_s = astman_get_header(m, "Penalty");
07315    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
07316    queuename = astman_get_header(m, "Queue");
07317 
07318    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07319       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07320       return 0;
07321    }
07322  
07323    penalty = atoi(penalty_s);
07324 
07325    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07326       astman_send_error(s, m, "Invalid interface, queuename or penalty");
07327    else
07328       astman_send_ack(s, m, "Interface penalty set successfully");
07329 
07330    return 0;
07331 }

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

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

07231 {
07232    struct ast_flags mask = {0,};
07233    const char *queuename = NULL;
07234    int header_found = 0;
07235 
07236    queuename = astman_get_header(m, "Queue");
07237    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07238       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07239       header_found = 1;
07240    }
07241    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07242       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07243       header_found = 1;
07244    }
07245    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07246       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07247       header_found = 1;
07248    }
07249 
07250    if (!header_found) {
07251       ast_set_flag(&mask, AST_FLAGS_ALL);
07252    }
07253 
07254    if (!reload_handler(1, &mask, queuename)) {
07255       astman_send_ack(s, m, "Queue reloaded successfully");
07256    } else {
07257       astman_send_error(s, m, "Error encountered while reloading queue");
07258    }
07259    return 0;
07260 }

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

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

07263 {
07264    const char *queuename = NULL;
07265    struct ast_flags mask = {QUEUE_RESET_STATS,};
07266    
07267    queuename = astman_get_header(m, "Queue");
07268 
07269    if (!reload_handler(1, &mask, queuename)) {
07270       astman_send_ack(s, m, "Queue stats reset successfully");
07271    } else {
07272       astman_send_error(s, m, "Error encountered while resetting queue stats");
07273    }
07274    return 0;
07275 }

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

Definition at line 6903 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::list, 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().

06904 {
06905    const char *rule = astman_get_header(m, "Rule");
06906    struct rule_list *rl_iter;
06907    struct penalty_rule *pr_iter;
06908 
06909    AST_LIST_LOCK(&rule_lists);
06910    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06911       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
06912          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
06913          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06914             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 );
06915          }
06916          if (!ast_strlen_zero(rule))
06917             break;
06918       }
06919    }
06920    AST_LIST_UNLOCK(&rule_lists);
06921 
06922    astman_append(s, "\r\n\r\n");
06923 
06924    return RESULT_SUCCESS;
06925 }

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

Definition at line 6893 of file app_queue.c.

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

Referenced by load_module().

06894 {
06895    static const char * const a[] = { "queue", "show" };
06896 
06897    __queues_show(s, -1, 2, a);
06898    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
06899 
06900    return RESULT_SUCCESS;
06901 }

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

Queue status info via AMI.

Definition at line 7003 of file app_queue.c.

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

Referenced by load_module().

07004 {
07005    time_t now;
07006    int pos;
07007    const char *id = astman_get_header(m,"ActionID");
07008    const char *queuefilter = astman_get_header(m,"Queue");
07009    const char *memberfilter = astman_get_header(m,"Member");
07010    char idText[256] = "";
07011    struct call_queue *q;
07012    struct queue_ent *qe;
07013    float sl = 0;
07014    struct member *mem;
07015    struct ao2_iterator queue_iter;
07016    struct ao2_iterator mem_iter;
07017 
07018    astman_send_ack(s, m, "Queue status will follow");
07019    time(&now);
07020    if (!ast_strlen_zero(id))
07021       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07022 
07023    queue_iter = ao2_iterator_init(queues, 0);
07024    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07025       ao2_lock(q);
07026 
07027       /* List queue properties */
07028       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07029          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07030          astman_append(s, "Event: QueueParams\r\n"
07031             "Queue: %s\r\n"
07032             "Max: %d\r\n"
07033             "Strategy: %s\r\n"
07034             "Calls: %d\r\n"
07035             "Holdtime: %d\r\n"
07036             "TalkTime: %d\r\n"
07037             "Completed: %d\r\n"
07038             "Abandoned: %d\r\n"
07039             "ServiceLevel: %d\r\n"
07040             "ServicelevelPerf: %2.1f\r\n"
07041             "Weight: %d\r\n"
07042             "%s"
07043             "\r\n",
07044             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07045             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07046          /* List Queue Members */
07047          mem_iter = ao2_iterator_init(q->members, 0);
07048          while ((mem = ao2_iterator_next(&mem_iter))) {
07049             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07050                astman_append(s, "Event: QueueMember\r\n"
07051                   "Queue: %s\r\n"
07052                   "Name: %s\r\n"
07053                   "Location: %s\r\n"
07054                   "Membership: %s\r\n"
07055                   "Penalty: %d\r\n"
07056                   "CallsTaken: %d\r\n"
07057                   "LastCall: %d\r\n"
07058                   "Status: %d\r\n"
07059                   "Paused: %d\r\n"
07060                   "%s"
07061                   "\r\n",
07062                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07063                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07064             }
07065             ao2_ref(mem, -1);
07066          }
07067          ao2_iterator_destroy(&mem_iter);
07068          /* List Queue Entries */
07069          pos = 1;
07070          for (qe = q->head; qe; qe = qe->next) {
07071             astman_append(s, "Event: QueueEntry\r\n"
07072                "Queue: %s\r\n"
07073                "Position: %d\r\n"
07074                "Channel: %s\r\n"
07075                "Uniqueid: %s\r\n"
07076                "CallerIDNum: %s\r\n"
07077                "CallerIDName: %s\r\n"
07078                "Wait: %ld\r\n"
07079                "%s"
07080                "\r\n",
07081                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07082                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07083                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07084                (long) (now - qe->start), idText);
07085          }
07086       }
07087       ao2_unlock(q);
07088       queue_t_unref(q, "Done with iterator");
07089    }
07090    ao2_iterator_destroy(&queue_iter);
07091 
07092    astman_append(s,
07093       "Event: QueueStatusComplete\r\n"
07094       "%s"
07095       "\r\n",idText);
07096 
07097    return RESULT_SUCCESS;
07098 }

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

Summary of queue info via the AMI.

Definition at line 6928 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, RESULT_SUCCESS, queue_ent::start, member::status, and call_queue::talktime.

Referenced by load_module().

06929 {
06930    time_t now;
06931    int qmemcount = 0;
06932    int qmemavail = 0;
06933    int qchancount = 0;
06934    int qlongestholdtime = 0;
06935    const char *id = astman_get_header(m, "ActionID");
06936    const char *queuefilter = astman_get_header(m, "Queue");
06937    char idText[256] = "";
06938    struct call_queue *q;
06939    struct queue_ent *qe;
06940    struct member *mem;
06941    struct ao2_iterator queue_iter;
06942    struct ao2_iterator mem_iter;
06943 
06944    astman_send_ack(s, m, "Queue summary will follow");
06945    time(&now);
06946    if (!ast_strlen_zero(id))
06947       snprintf(idText, 256, "ActionID: %s\r\n", id);
06948    queue_iter = ao2_iterator_init(queues, 0);
06949    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06950       ao2_lock(q);
06951 
06952       /* List queue properties */
06953       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06954          /* Reset the necessary local variables if no queuefilter is set*/
06955          qmemcount = 0;
06956          qmemavail = 0;
06957          qchancount = 0;
06958          qlongestholdtime = 0;
06959 
06960          /* List Queue Members */
06961          mem_iter = ao2_iterator_init(q->members, 0);
06962          while ((mem = ao2_iterator_next(&mem_iter))) {
06963             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
06964                ++qmemcount;
06965                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
06966                   ++qmemavail;
06967                }
06968             }
06969             ao2_ref(mem, -1);
06970          }
06971          ao2_iterator_destroy(&mem_iter);
06972          for (qe = q->head; qe; qe = qe->next) {
06973             if ((now - qe->start) > qlongestholdtime) {
06974                qlongestholdtime = now - qe->start;
06975             }
06976             ++qchancount;
06977          }
06978          astman_append(s, "Event: QueueSummary\r\n"
06979             "Queue: %s\r\n"
06980             "LoggedIn: %d\r\n"
06981             "Available: %d\r\n"
06982             "Callers: %d\r\n" 
06983             "HoldTime: %d\r\n"
06984             "TalkTime: %d\r\n"
06985             "LongestHoldTime: %d\r\n"
06986             "%s"
06987             "\r\n",
06988             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
06989       }
06990       ao2_unlock(q);
06991       queue_t_unref(q, "Done with iterator");
06992    }
06993    ao2_iterator_destroy(&queue_iter);
06994    astman_append(s,
06995       "Event: QueueSummaryComplete\r\n"
06996       "%s"
06997       "\r\n", idText);
06998 
06999    return RESULT_SUCCESS;
07000 }

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

Definition at line 7151 of file app_queue.c.

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

Referenced by load_module().

07152 {
07153    const char *queuename, *interface;
07154 
07155    queuename = astman_get_header(m, "Queue");
07156    interface = astman_get_header(m, "Interface");
07157 
07158    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07159       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07160       return 0;
07161    }
07162 
07163    switch (remove_from_queue(queuename, interface)) {
07164    case RES_OKAY:
07165       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07166       astman_send_ack(s, m, "Removed interface from queue");
07167       break;
07168    case RES_EXISTS:
07169       astman_send_error(s, m, "Unable to remove interface: Not there");
07170       break;
07171    case RES_NOSUCHQUEUE:
07172       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07173       break;
07174    case RES_OUTOFMEMORY:
07175       astman_send_error(s, m, "Out of memory");
07176       break;
07177    case RES_NOT_DYNAMIC:
07178       astman_send_error(s, m, "Member not dynamic");
07179       break;
07180    }
07181 
07182    return 0;
07183 }

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

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

06565 {
06566    struct call_queue *q = obj;
06567    char *queuename = arg;
06568    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
06569       q->dead = 1;
06570       q->found = 0;
06571    }
06572    return 0;
06573 }

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

Definition at line 6428 of file app_queue.c.

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

Referenced by reload_single_queue().

06429 {
06430    struct member *member = obj;
06431    if (!member->dynamic) {
06432       member->delme = 1;
06433    }
06434    return 0;
06435 }

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

Definition at line 1492 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01493 {
01494    struct member *mem1 = obj1, *mem2 = obj2;
01495    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01496 }

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

Definition at line 1480 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01481 {
01482    const struct member *mem = obj;
01483    const char *chname = strchr(mem->interface, '/');
01484    int ret = 0, i;
01485    if (!chname)
01486       chname = mem->interface;
01487    for (i = 0; i < 5 && chname[i]; i++)
01488       ret += compress_char(chname[i]) << (i * 6);
01489    return ret;
01490 }

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

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

Referenced by compare_weight(), and is_our_turn().

02701 {
02702    struct member *mem;
02703    int avl = 0;
02704    struct ao2_iterator mem_iter;
02705 
02706    mem_iter = ao2_iterator_init(q->members, 0);
02707    while ((mem = ao2_iterator_next(&mem_iter))) {
02708       switch (mem->status) {
02709       case AST_DEVICE_INUSE:
02710          if (!q->ringinuse)
02711             break;
02712          /* else fall through */
02713       case AST_DEVICE_NOT_INUSE:
02714       case AST_DEVICE_UNKNOWN:
02715          if (!mem->paused) {
02716             avl++;
02717          }
02718          break;
02719       }
02720       ao2_ref(mem, -1);
02721 
02722       /* If autofill is not enabled or if the queue's strategy is ringall, then
02723        * we really don't care about the number of available members so much as we
02724        * do that there is at least one available.
02725        *
02726        * In fact, we purposely will return from this function stating that only
02727        * one member is available if either of those conditions hold. That way,
02728        * functions which determine what action to take based on the number of available
02729        * members will operate properly. The reasoning is that even if multiple
02730        * members are available, only the head caller can actually be serviced.
02731        */
02732       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02733          break;
02734       }
02735    }
02736    ao2_iterator_destroy(&mem_iter);
02737 
02738    return avl;
02739 }

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

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

01673 {
01674    char *value_copy = ast_strdupa(value);
01675    char *option = NULL;
01676    while ((option = strsep(&value_copy, ","))) {
01677       if (!strcasecmp(option, "paused")) {
01678          *empty |= QUEUE_EMPTY_PAUSED;
01679       } else if (!strcasecmp(option, "penalty")) {
01680          *empty |= QUEUE_EMPTY_PENALTY;
01681       } else if (!strcasecmp(option, "inuse")) {
01682          *empty |= QUEUE_EMPTY_INUSE;
01683       } else if (!strcasecmp(option, "ringing")) {
01684          *empty |= QUEUE_EMPTY_RINGING;
01685       } else if (!strcasecmp(option, "invalid")) {
01686          *empty |= QUEUE_EMPTY_INVALID;
01687       } else if (!strcasecmp(option, "wrapup")) {
01688          *empty |= QUEUE_EMPTY_WRAPUP;
01689       } else if (!strcasecmp(option, "unavailable")) {
01690          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01691       } else if (!strcasecmp(option, "unknown")) {
01692          *empty |= QUEUE_EMPTY_UNKNOWN;
01693       } else if (!strcasecmp(option, "loose")) {
01694          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01695       } else if (!strcasecmp(option, "strict")) {
01696          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01697       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01698          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01699       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01700          *empty = 0;
01701       } else {
01702          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01703       }
01704    }
01705 }

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

Definition at line 2370 of file app_queue.c.

References AST_DIGIT_ANY, ast_fileexists(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), queue_ent::chan, and ast_channel::language.

Referenced by say_periodic_announcement(), and say_position().

02371 {
02372    int res;
02373 
02374    if (ast_strlen_zero(filename)) {
02375       return 0;
02376    }
02377 
02378    if (!ast_fileexists(filename, NULL, chan->language)) {
02379       return 0;
02380    }
02381 
02382    ast_stopstream(chan);
02383 
02384    res = ast_streamfile(chan, filename, chan->language);
02385    if (!res)
02386       res = ast_waitstream(chan, AST_DIGIT_ANY);
02387 
02388    ast_stopstream(chan);
02389 
02390    return res;
02391 }

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

PauseQueueMember application.

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

05348 {
05349    char *parse;
05350    AST_DECLARE_APP_ARGS(args,
05351       AST_APP_ARG(queuename);
05352       AST_APP_ARG(interface);
05353       AST_APP_ARG(options);
05354       AST_APP_ARG(reason);
05355    );
05356 
05357    if (ast_strlen_zero(data)) {
05358       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05359       return -1;
05360    }
05361 
05362    parse = ast_strdupa(data);
05363 
05364    AST_STANDARD_APP_ARGS(args, parse);
05365 
05366    if (ast_strlen_zero(args.interface)) {
05367       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05368       return -1;
05369    }
05370 
05371    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05372       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05373       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05374       return 0;
05375    }
05376 
05377    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05378 
05379    return 0;
05380 }

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

QueueLog application.

Definition at line 5539 of file app_queue.c.

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

Referenced by load_module().

05540 {
05541    char *parse;
05542 
05543    AST_DECLARE_APP_ARGS(args,
05544       AST_APP_ARG(queuename);
05545       AST_APP_ARG(uniqueid);
05546       AST_APP_ARG(membername);
05547       AST_APP_ARG(event);
05548       AST_APP_ARG(params);
05549    );
05550 
05551    if (ast_strlen_zero(data)) {
05552       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
05553       return -1;
05554    }
05555 
05556    parse = ast_strdupa(data);
05557 
05558    AST_STANDARD_APP_ARGS(args, parse);
05559 
05560    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
05561        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
05562       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
05563       return -1;
05564    }
05565 
05566    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
05567       "%s", args.params ? args.params : "");
05568 
05569    return 0;
05570 }

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

Definition at line 1100 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

01101 {
01102    struct call_queue *q = obj, *q2 = arg;
01103    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
01104 }

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

References call_queue::announcefrequency, args, AST_APP_ARG, ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_FIRST, ast_log(), ast_moh_start(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::chan, copy_rules(), queue_ent::expire, get_member_status(), 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, call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::min_penalty, queue_ent::moh, call_queue::name, ast_channel::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_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, status, stop, ast_party_number::str, penalty_rule::time, try_calling(), ast_channel::uniqueid, 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().

05615 {
05616    int res=-1;
05617    int ringing=0;
05618    const char *user_priority;
05619    const char *max_penalty_str;
05620    const char *min_penalty_str;
05621    int prio;
05622    int qcontinue = 0;
05623    int max_penalty, min_penalty;
05624    enum queue_result reason = QUEUE_UNKNOWN;
05625    /* whether to exit Queue application after the timeout hits */
05626    int tries = 0;
05627    int noption = 0;
05628    char *parse;
05629    int makeannouncement = 0;
05630    int position = 0;
05631    AST_DECLARE_APP_ARGS(args,
05632       AST_APP_ARG(queuename);
05633       AST_APP_ARG(options);
05634       AST_APP_ARG(url);
05635       AST_APP_ARG(announceoverride);
05636       AST_APP_ARG(queuetimeoutstr);
05637       AST_APP_ARG(agi);
05638       AST_APP_ARG(macro);
05639       AST_APP_ARG(gosub);
05640       AST_APP_ARG(rule);
05641       AST_APP_ARG(position);
05642    );
05643    /* Our queue entry */
05644    struct queue_ent qe = { 0 };
05645    
05646    if (ast_strlen_zero(data)) {
05647       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
05648       return -1;
05649    }
05650    
05651    parse = ast_strdupa(data);
05652    AST_STANDARD_APP_ARGS(args, parse);
05653 
05654    /* Setup our queue entry */
05655    qe.start = time(NULL);
05656 
05657    /* set the expire time based on the supplied timeout; */
05658    if (!ast_strlen_zero(args.queuetimeoutstr))
05659       qe.expire = qe.start + atoi(args.queuetimeoutstr);
05660    else
05661       qe.expire = 0;
05662 
05663    /* Get the priority from the variable ${QUEUE_PRIO} */
05664    ast_channel_lock(chan);
05665    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
05666    if (user_priority) {
05667       if (sscanf(user_priority, "%30d", &prio) == 1) {
05668          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
05669       } else {
05670          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
05671             user_priority, chan->name);
05672          prio = 0;
05673       }
05674    } else {
05675       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
05676       prio = 0;
05677    }
05678 
05679    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
05680 
05681    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
05682       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
05683          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
05684       } else {
05685          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
05686             max_penalty_str, chan->name);
05687          max_penalty = 0;
05688       }
05689    } else {
05690       max_penalty = 0;
05691    }
05692 
05693    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
05694       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
05695          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
05696       } else {
05697          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
05698             min_penalty_str, chan->name);
05699          min_penalty = 0;
05700       }
05701    } else {
05702       min_penalty = 0;
05703    }
05704    ast_channel_unlock(chan);
05705 
05706    if (args.options && (strchr(args.options, 'r')))
05707       ringing = 1;
05708 
05709    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
05710       qe.ring_when_ringing = 1;
05711    }
05712 
05713    if (args.options && (strchr(args.options, 'c')))
05714       qcontinue = 1;
05715 
05716    if (args.position) {
05717       position = atoi(args.position);
05718       if (position < 0) {
05719          ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
05720          position = 0;
05721       }
05722    }
05723 
05724    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
05725       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
05726 
05727    qe.chan = chan;
05728    qe.prio = prio;
05729    qe.max_penalty = max_penalty;
05730    qe.min_penalty = min_penalty;
05731    qe.last_pos_said = 0;
05732    qe.last_pos = 0;
05733    qe.last_periodic_announce_time = time(NULL);
05734    qe.last_periodic_announce_sound = 0;
05735    qe.valid_digits = 0;
05736    if (join_queue(args.queuename, &qe, &reason, position)) {
05737       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
05738       set_queue_result(chan, reason);
05739       return 0;
05740    }
05741    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
05742       S_OR(args.url, ""),
05743       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
05744       qe.opos);
05745    copy_rules(&qe, args.rule);
05746    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
05747 check_turns:
05748    if (ringing) {
05749       ast_indicate(chan, AST_CONTROL_RINGING);
05750    } else {
05751       ast_moh_start(chan, qe.moh, NULL);
05752    }
05753 
05754    /* This is the wait loop for callers 2 through maxlen */
05755    res = wait_our_turn(&qe, ringing, &reason);
05756    if (res) {
05757       goto stop;
05758    }
05759 
05760    makeannouncement = 0;
05761 
05762    for (;;) {
05763       /* This is the wait loop for the head caller*/
05764       /* To exit, they may get their call answered; */
05765       /* they may dial a digit from the queue context; */
05766       /* or, they may timeout. */
05767 
05768       /* Leave if we have exceeded our queuetimeout */
05769       if (qe.expire && (time(NULL) >= qe.expire)) {
05770          record_abandoned(&qe);
05771          reason = QUEUE_TIMEOUT;
05772          res = 0;
05773          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
05774             qe.pos, qe.opos, (long) time(NULL) - qe.start);
05775          break;
05776       }
05777 
05778       if (makeannouncement) {
05779          /* Make a position announcement, if enabled */
05780          if (qe.parent->announcefrequency)
05781             if ((res = say_position(&qe,ringing)))
05782                goto stop;
05783       }
05784       makeannouncement = 1;
05785 
05786       /* Make a periodic announcement, if enabled */
05787       if (qe.parent->periodicannouncefrequency)
05788          if ((res = say_periodic_announcement(&qe,ringing)))
05789             goto stop;
05790    
05791       /* Leave if we have exceeded our queuetimeout */
05792       if (qe.expire && (time(NULL) >= qe.expire)) {
05793          record_abandoned(&qe);
05794          reason = QUEUE_TIMEOUT;
05795          res = 0;
05796          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05797          break;
05798       }
05799 
05800       /* see if we need to move to the next penalty level for this queue */
05801       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
05802          update_qe_rule(&qe);
05803       }
05804 
05805       /* Try calling all queue members for 'timeout' seconds */
05806       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
05807       if (res) {
05808          goto stop;
05809       }
05810 
05811       if (qe.parent->leavewhenempty) {
05812          int status = 0;
05813          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
05814             record_abandoned(&qe);
05815             reason = QUEUE_LEAVEEMPTY;
05816             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05817             res = 0;
05818             break;
05819          }
05820       }
05821 
05822       /* exit after 'timeout' cycle if 'n' option enabled */
05823       if (noption && tries >= qe.parent->membercount) {
05824          ast_verb(3, "Exiting on time-out cycle\n");
05825          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05826          record_abandoned(&qe);
05827          reason = QUEUE_TIMEOUT;
05828          res = 0;
05829          break;
05830       }
05831 
05832       
05833       /* Leave if we have exceeded our queuetimeout */
05834       if (qe.expire && (time(NULL) >= qe.expire)) {
05835          record_abandoned(&qe);
05836          reason = QUEUE_TIMEOUT;
05837          res = 0;
05838          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
05839          break;
05840       }
05841 
05842       /* If using dynamic realtime members, we should regenerate the member list for this queue */
05843       update_realtime_members(qe.parent);
05844       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
05845       res = wait_a_bit(&qe);
05846       if (res)
05847          goto stop;
05848 
05849       /* Since this is a priority queue and
05850        * it is not sure that we are still at the head
05851        * of the queue, go and check for our turn again.
05852        */
05853       if (!is_our_turn(&qe)) {
05854          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
05855          goto check_turns;
05856       }
05857    }
05858 
05859 stop:
05860    if (res) {
05861       if (res < 0) {
05862          if (!qe.handled) {
05863             record_abandoned(&qe);
05864             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
05865                "%d|%d|%ld", qe.pos, qe.opos,
05866                (long) time(NULL) - qe.start);
05867             res = -1;
05868          } else if (qcontinue) {
05869             reason = QUEUE_CONTINUE;
05870             res = 0;
05871          }
05872       } else if (qe.valid_digits) {
05873          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05874             "%s|%d", qe.digits, qe.pos);
05875       }
05876    }
05877 
05878    /* Don't allow return code > 0 */
05879    if (res >= 0) {
05880       res = 0; 
05881       if (ringing) {
05882          ast_indicate(chan, -1);
05883       } else {
05884          ast_moh_stop(chan);
05885       }        
05886       ast_stopstream(chan);
05887    }
05888 
05889    set_queue_variables(qe.parent, qe.chan);
05890 
05891    leave_queue(&qe);
05892    if (reason != QUEUE_UNKNOWN)
05893       set_queue_result(chan, reason);
05894 
05895    if (qe.parent) {
05896       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
05897        * This ref must be taken away right before the queue_ent is destroyed.  In this case
05898        * the queue_ent is about to be returned on the stack */
05899       qe.parent = queue_unref(qe.parent);
05900    }
05901 
05902    return res;
05903 }

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

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

05958 {
05959    struct call_queue *q;
05960 
05961    buf[0] = '\0';
05962 
05963    if (ast_strlen_zero(data)) {
05964       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05965       return -1;
05966    }
05967    q = load_realtime_queue(data);
05968    snprintf(buf, len, "%d", q != NULL? 1 : 0);
05969    if (q) {
05970       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
05971    }
05972 
05973    return 0;
05974 }

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

06176 {
06177    int penalty;
06178    AST_DECLARE_APP_ARGS(args,
06179       AST_APP_ARG(queuename);
06180       AST_APP_ARG(interface);
06181    );
06182    /* Make sure the returned value on error is NULL. */
06183    buf[0] = '\0';
06184 
06185    if (ast_strlen_zero(data)) {
06186       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06187       return -1;
06188    }
06189 
06190    AST_STANDARD_APP_ARGS(args, data);
06191 
06192    if (args.argc < 2) {
06193       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06194       return -1;
06195    }
06196 
06197    penalty = get_member_penalty (args.queuename, args.interface);
06198    
06199    if (penalty >= 0) /* remember that buf is already '\0' */
06200       snprintf (buf, len, "%d", penalty);
06201 
06202    return 0;
06203 }

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

06207 {
06208    int penalty;
06209    AST_DECLARE_APP_ARGS(args,
06210       AST_APP_ARG(queuename);
06211       AST_APP_ARG(interface);
06212    );
06213 
06214    if (ast_strlen_zero(data)) {
06215       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06216       return -1;
06217    }
06218 
06219    AST_STANDARD_APP_ARGS(args, data);
06220 
06221    if (args.argc < 2) {
06222       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06223       return -1;
06224    }
06225 
06226    penalty = atoi(value);
06227 
06228    if (ast_strlen_zero(args.interface)) {
06229       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06230       return -1;
06231    }
06232 
06233    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06234    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06235       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06236       return -1;
06237    }
06238 
06239    return 0;
06240 }

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

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

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

Definition at line 5981 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_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, member::lastcall, load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_t_unref, and member::status.

05982 {
05983    int count = 0;
05984    struct member *m;
05985    struct ao2_iterator mem_iter;
05986    struct call_queue *q;
05987    char *option;
05988 
05989    if (ast_strlen_zero(data)) {
05990       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05991       return -1;
05992    }
05993 
05994    if ((option = strchr(data, ',')))
05995       *option++ = '\0';
05996    else
05997       option = "logged";
05998    if ((q = load_realtime_queue(data))) {
05999       ao2_lock(q);
06000       if (!strcasecmp(option, "logged")) {
06001          mem_iter = ao2_iterator_init(q->members, 0);
06002          while ((m = ao2_iterator_next(&mem_iter))) {
06003             /* Count the agents who are logged in and presently answering calls */
06004             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06005                count++;
06006             }
06007             ao2_ref(m, -1);
06008          }
06009          ao2_iterator_destroy(&mem_iter);
06010       } else if (!strcasecmp(option, "free")) {
06011          mem_iter = ao2_iterator_init(q->members, 0);
06012          while ((m = ao2_iterator_next(&mem_iter))) {
06013             /* Count the agents who are logged in and presently answering calls */
06014             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06015                count++;
06016             }
06017             ao2_ref(m, -1);
06018          }
06019          ao2_iterator_destroy(&mem_iter);
06020       } else if (!strcasecmp(option, "ready")) {
06021          time_t now;
06022          time(&now);
06023          mem_iter = ao2_iterator_init(q->members, 0);
06024          while ((m = ao2_iterator_next(&mem_iter))) {
06025             /* Count the agents who are logged in, not paused and not wrapping up */
06026             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06027                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06028                count++;
06029             }
06030             ao2_ref(m, -1);
06031          }
06032          ao2_iterator_destroy(&mem_iter);
06033       } else /* must be "count" */
06034          count = q->membercount;
06035       ao2_unlock(q);
06036       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06037    } else
06038       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06039 
06040    snprintf(buf, len, "%d", count);
06041 
06042    return 0;
06043 }

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

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

06051 {
06052    int count = 0;
06053    struct member *m;
06054    struct call_queue *q;
06055    struct ao2_iterator mem_iter;
06056    static int depflag = 1;
06057 
06058    if (depflag) {
06059       depflag = 0;
06060       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");
06061    }
06062 
06063    if (ast_strlen_zero(data)) {
06064       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06065       return -1;
06066    }
06067    
06068    if ((q = load_realtime_queue(data))) {
06069       ao2_lock(q);
06070       mem_iter = ao2_iterator_init(q->members, 0);
06071       while ((m = ao2_iterator_next(&mem_iter))) {
06072          /* Count the agents who are logged in and presently answering calls */
06073          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06074             count++;
06075          }
06076          ao2_ref(m, -1);
06077       }
06078       ao2_iterator_destroy(&mem_iter);
06079       ao2_unlock(q);
06080       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06081    } else
06082       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06083 
06084    snprintf(buf, len, "%d", count);
06085 
06086    return 0;
06087 }

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 6126 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, and queue_t_unref.

06127 {
06128    struct call_queue *q, tmpq = {
06129       .name = data,  
06130    };
06131    struct member *m;
06132 
06133    /* Ensure an otherwise empty list doesn't return garbage */
06134    buf[0] = '\0';
06135 
06136    if (ast_strlen_zero(data)) {
06137       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06138       return -1;
06139    }
06140 
06141    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06142       int buflen = 0, count = 0;
06143       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
06144 
06145       ao2_lock(q);
06146       while ((m = ao2_iterator_next(&mem_iter))) {
06147          /* strcat() is always faster than printf() */
06148          if (count++) {
06149             strncat(buf + buflen, ",", len - buflen - 1);
06150             buflen++;
06151          }
06152          strncat(buf + buflen, m->interface, len - buflen - 1);
06153          buflen += strlen(m->interface);
06154          /* Safeguard against overflow (negative length) */
06155          if (buflen >= len - 2) {
06156             ao2_ref(m, -1);
06157             ast_log(LOG_WARNING, "Truncating list\n");
06158             break;
06159          }
06160          ao2_ref(m, -1);
06161       }
06162       ao2_iterator_destroy(&mem_iter);
06163       ao2_unlock(q);
06164       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06165    } else
06166       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06167 
06168    /* We should already be terminated, but let's make sure. */
06169    buf[len - 1] = '\0';
06170 
06171    return 0;
06172 }

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 6090 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, SENTINEL, and var.

06091 {
06092    int count = 0;
06093    struct call_queue *q, tmpq = {
06094       .name = data,  
06095    };
06096    struct ast_variable *var = NULL;
06097 
06098    buf[0] = '\0';
06099    
06100    if (ast_strlen_zero(data)) {
06101       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06102       return -1;
06103    }
06104 
06105    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06106       ao2_lock(q);
06107       count = q->count;
06108       ao2_unlock(q);
06109       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06110    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06111       /* if the queue is realtime but was not found in memory, this
06112        * means that the queue had been deleted from memory since it was 
06113        * "dead." This means it has a 0 waiting count
06114        */
06115       count = 0;
06116       ast_variables_destroy(var);
06117    } else
06118       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06119 
06120    snprintf(buf, len, "%d", count);
06121 
06122    return 0;
06123 }

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 5910 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, call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.

05911 {
05912    int res = -1;
05913    struct call_queue *q, tmpq = {
05914       .name = data,  
05915    };
05916 
05917    char interfacevar[256] = "";
05918    float sl = 0;
05919 
05920    if (ast_strlen_zero(data)) {
05921       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05922       return -1;
05923    }
05924 
05925    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
05926       ao2_lock(q);
05927       if (q->setqueuevar) {
05928          sl = 0;
05929          res = 0;
05930 
05931          if (q->callscompleted > 0) {
05932             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05933          }
05934 
05935          snprintf(interfacevar, sizeof(interfacevar),
05936             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05937             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05938 
05939          pbx_builtin_setvar_multiple(chan, interfacevar);
05940       }
05941 
05942       ao2_unlock(q);
05943       queue_t_unref(q, "Done with QUEUE() function");
05944    } else {
05945       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05946    }
05947 
05948    snprintf(buf, len, "%d", res);
05949 
05950    return 0;
05951 }

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

Definition at line 1093 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

01094 {
01095    const struct call_queue *q = obj;
01096 
01097    return ast_str_case_hash(q->name);
01098 }

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

Definition at line 1118 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01119 {
01120    ao2_ref(q, 1);
01121    return q;
01122 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06333 {
06334    const char *general_val = NULL;
06335    queue_persistent_members = 0;
06336    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06337       queue_persistent_members = ast_true(general_val);
06338    autofill_default = 0;
06339    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06340       autofill_default = ast_true(general_val);
06341    montype_default = 0;
06342    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06343       if (!strcasecmp(general_val, "mixmonitor"))
06344          montype_default = 1;
06345    }
06346    update_cdr = 0;
06347    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06348       update_cdr = ast_true(general_val);
06349    shared_lastcall = 0;
06350    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06351       shared_lastcall = ast_true(general_val);
06352 }

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

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

01716 {
01717    if (!strcasecmp(param, "musicclass") || 
01718       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01719       ast_string_field_set(q, moh, val);
01720    } else if (!strcasecmp(param, "announce")) {
01721       ast_string_field_set(q, announce, val);
01722    } else if (!strcasecmp(param, "context")) {
01723       ast_string_field_set(q, context, val);
01724    } else if (!strcasecmp(param, "timeout")) {
01725       q->timeout = atoi(val);
01726       if (q->timeout < 0)
01727          q->timeout = DEFAULT_TIMEOUT;
01728    } else if (!strcasecmp(param, "ringinuse")) {
01729       q->ringinuse = ast_true(val);
01730    } else if (!strcasecmp(param, "setinterfacevar")) {
01731       q->setinterfacevar = ast_true(val);
01732    } else if (!strcasecmp(param, "setqueuevar")) {
01733       q->setqueuevar = ast_true(val);
01734    } else if (!strcasecmp(param, "setqueueentryvar")) {
01735       q->setqueueentryvar = ast_true(val);
01736    } else if (!strcasecmp(param, "monitor-format")) {
01737       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01738    } else if (!strcasecmp(param, "membermacro")) {
01739       ast_string_field_set(q, membermacro, val);
01740    } else if (!strcasecmp(param, "membergosub")) {
01741       ast_string_field_set(q, membergosub, val);
01742    } else if (!strcasecmp(param, "queue-youarenext")) {
01743       ast_string_field_set(q, sound_next, val);
01744    } else if (!strcasecmp(param, "queue-thereare")) {
01745       ast_string_field_set(q, sound_thereare, val);
01746    } else if (!strcasecmp(param, "queue-callswaiting")) {
01747       ast_string_field_set(q, sound_calls, val);
01748    } else if (!strcasecmp(param, "queue-quantity1")) {
01749       ast_string_field_set(q, queue_quantity1, val);
01750    } else if (!strcasecmp(param, "queue-quantity2")) {
01751       ast_string_field_set(q, queue_quantity2, val);
01752    } else if (!strcasecmp(param, "queue-holdtime")) {
01753       ast_string_field_set(q, sound_holdtime, val);
01754    } else if (!strcasecmp(param, "queue-minutes")) {
01755       ast_string_field_set(q, sound_minutes, val);
01756    } else if (!strcasecmp(param, "queue-minute")) {
01757       ast_string_field_set(q, sound_minute, val);
01758    } else if (!strcasecmp(param, "queue-seconds")) {
01759       ast_string_field_set(q, sound_seconds, val);
01760    } else if (!strcasecmp(param, "queue-thankyou")) {
01761       ast_string_field_set(q, sound_thanks, val);
01762    } else if (!strcasecmp(param, "queue-callerannounce")) {
01763       ast_string_field_set(q, sound_callerannounce, val);
01764    } else if (!strcasecmp(param, "queue-reporthold")) {
01765       ast_string_field_set(q, sound_reporthold, val);
01766    } else if (!strcasecmp(param, "announce-frequency")) {
01767       q->announcefrequency = atoi(val);
01768    } else if (!strcasecmp(param, "min-announce-frequency")) {
01769       q->minannouncefrequency = atoi(val);
01770       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01771    } else if (!strcasecmp(param, "announce-round-seconds")) {
01772       q->roundingseconds = atoi(val);
01773       /* Rounding to any other values just doesn't make sense... */
01774       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01775          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01776          if (linenum >= 0) {
01777             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01778                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01779                val, param, q->name, linenum);
01780          } else {
01781             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01782                "using 0 instead for queue '%s'\n", val, param, q->name);
01783          }
01784          q->roundingseconds=0;
01785       }
01786    } else if (!strcasecmp(param, "announce-holdtime")) {
01787       if (!strcasecmp(val, "once"))
01788          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01789       else if (ast_true(val))
01790          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01791       else
01792          q->announceholdtime = 0;
01793    } else if (!strcasecmp(param, "announce-position")) {
01794       if (!strcasecmp(val, "limit"))
01795          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01796       else if (!strcasecmp(val, "more"))
01797          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01798       else if (ast_true(val))
01799          q->announceposition = ANNOUNCEPOSITION_YES;
01800       else
01801          q->announceposition = ANNOUNCEPOSITION_NO;
01802    } else if (!strcasecmp(param, "announce-position-limit")) {
01803       q->announcepositionlimit = atoi(val);
01804    } else if (!strcasecmp(param, "periodic-announce")) {
01805       if (strchr(val, ',')) {
01806          char *s, *buf = ast_strdupa(val);
01807          unsigned int i = 0;
01808 
01809          while ((s = strsep(&buf, ",|"))) {
01810             if (!q->sound_periodicannounce[i])
01811                q->sound_periodicannounce[i] = ast_str_create(16);
01812             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01813             i++;
01814             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01815                break;
01816          }
01817          q->numperiodicannounce = i;
01818       } else {
01819          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01820          q->numperiodicannounce = 1;
01821       }
01822    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01823       q->periodicannouncefrequency = atoi(val);
01824    } else if (!strcasecmp(param, "relative-periodic-announce")) {
01825       q->relativeperiodicannounce = ast_true(val);
01826    } else if (!strcasecmp(param, "random-periodic-announce")) {
01827       q->randomperiodicannounce = ast_true(val);
01828    } else if (!strcasecmp(param, "retry")) {
01829       q->retry = atoi(val);
01830       if (q->retry <= 0)
01831          q->retry = DEFAULT_RETRY;
01832    } else if (!strcasecmp(param, "wrapuptime")) {
01833       q->wrapuptime = atoi(val);
01834    } else if (!strcasecmp(param, "penaltymemberslimit")) {
01835       if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
01836          q->penaltymemberslimit = 0;
01837       }
01838    } else if (!strcasecmp(param, "autofill")) {
01839       q->autofill = ast_true(val);
01840    } else if (!strcasecmp(param, "monitor-type")) {
01841       if (!strcasecmp(val, "mixmonitor"))
01842          q->montype = 1;
01843    } else if (!strcasecmp(param, "autopause")) {
01844       q->autopause = autopause2int(val);
01845    } else if (!strcasecmp(param, "maxlen")) {
01846       q->maxlen = atoi(val);
01847       if (q->maxlen < 0)
01848          q->maxlen = 0;
01849    } else if (!strcasecmp(param, "servicelevel")) {
01850       q->servicelevel= atoi(val);
01851    } else if (!strcasecmp(param, "strategy")) {
01852       int strategy;
01853 
01854       /* We are a static queue and already have set this, no need to do it again */
01855       if (failunknown) {
01856          return;
01857       }
01858       strategy = strat2int(val);
01859       if (strategy < 0) {
01860          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01861             val, q->name);
01862          q->strategy = QUEUE_STRATEGY_RINGALL;
01863       }
01864       if (strategy == q->strategy) {
01865          return;
01866       }
01867       if (strategy == QUEUE_STRATEGY_LINEAR) {
01868          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
01869          return;
01870       }
01871       q->strategy = strategy;
01872    } else if (!strcasecmp(param, "joinempty")) {
01873       parse_empty_options(val, &q->joinempty, 1);
01874    } else if (!strcasecmp(param, "leavewhenempty")) {
01875       parse_empty_options(val, &q->leavewhenempty, 0);
01876    } else if (!strcasecmp(param, "eventmemberstatus")) {
01877       q->maskmemberstatus = !ast_true(val);
01878    } else if (!strcasecmp(param, "eventwhencalled")) {
01879       if (!strcasecmp(val, "vars")) {
01880          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01881       } else {
01882          q->eventwhencalled = ast_true(val) ? 1 : 0;
01883       }
01884    } else if (!strcasecmp(param, "reportholdtime")) {
01885       q->reportholdtime = ast_true(val);
01886    } else if (!strcasecmp(param, "memberdelay")) {
01887       q->memberdelay = atoi(val);
01888    } else if (!strcasecmp(param, "weight")) {
01889       q->weight = atoi(val);
01890    } else if (!strcasecmp(param, "timeoutrestart")) {
01891       q->timeoutrestart = ast_true(val);
01892    } else if (!strcasecmp(param, "defaultrule")) {
01893       ast_string_field_set(q, defaultrule, val);
01894    } else if (!strcasecmp(param, "timeoutpriority")) {
01895       if (!strcasecmp(val, "conf")) {
01896          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
01897       } else {
01898          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01899       }
01900    } else if (failunknown) {
01901       if (linenum >= 0) {
01902          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01903             q->name, param, linenum);
01904       } else {
01905          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01906       }
01907    }
01908 }

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

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

06875 {
06876    switch ( cmd ) {
06877    case CLI_INIT:
06878       e->command = "queue show";
06879       e->usage =
06880          "Usage: queue show\n"
06881          "       Provides summary information on a specified queue.\n";
06882       return NULL;
06883    case CLI_GENERATE:
06884       return complete_queue_show(a->line, a->word, a->pos, a->n); 
06885    }
06886 
06887    return __queues_show(NULL, a->fd, a->argc, a->argv);
06888 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3997 of file app_queue.c.

References ast_free.

03998 {
03999    struct queue_transfer_ds *qtds = data;
04000    ast_free(qtds);
04001 }

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

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

04021 {
04022    struct queue_transfer_ds *qtds = data;
04023    struct queue_ent *qe = qtds->qe;
04024    struct member *member = qtds->member;
04025    time_t callstart = qtds->starttime;
04026    int callcompletedinsl = qtds->callcompletedinsl;
04027    struct ast_datastore *datastore;
04028 
04029    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04030             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04031             (long) (time(NULL) - callstart), qe->opos);
04032 
04033    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04034    
04035    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04036    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04037       ast_channel_datastore_remove(old_chan, datastore);
04038    } else {
04039       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04040    }
04041 }

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

Definition at line 1124 of file app_queue.c.

References ao2_ref.

Referenced by queues_data_provider_get().

01125 {
01126    ao2_ref(q, -1);
01127    return q;
01128 }

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

Definition at line 8020 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_load_realtime_multientry(), ast_strlen_zero(), load_realtime_queue(), queue_unref(), queues_data_provider_get_helper(), and SENTINEL.

08022 {
08023    struct ao2_iterator i;
08024    struct call_queue *queue, *queue_realtime = NULL;
08025    struct ast_config *cfg;
08026    char *queuename;
08027 
08028    /* load realtime queues. */
08029    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08030    if (cfg) {
08031       for (queuename = ast_category_browse(cfg, NULL);
08032             !ast_strlen_zero(queuename);
08033             queuename = ast_category_browse(cfg, queuename)) {
08034          if ((queue = load_realtime_queue(queuename))) {
08035             queue_unref(queue);
08036          }
08037       }
08038       ast_config_destroy(cfg);
08039    }
08040 
08041    /* static queues. */
08042    i = ao2_iterator_init(queues, 0);
08043    while ((queue = ao2_iterator_next(&i))) {
08044       ao2_lock(queue);
08045       if (queue->realtime && !(queue_realtime = load_realtime_queue(queue->name))) {
08046          ao2_unlock(queue);
08047          queue_unref(queue);
08048          continue;
08049       } else if (queue->realtime) {
08050          queue_unref(queue_realtime);
08051       }
08052 
08053       queues_data_provider_get_helper(search, data_root, queue);
08054       ao2_unlock(queue);
08055       queue_unref(queue);
08056    }
08057 
08058    return 0;
08059 }

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

References call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, 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().

07918 {
07919    struct ao2_iterator im;
07920    struct member *member;
07921    struct queue_ent *qe;
07922    struct ast_data *data_queue, *data_members = NULL, *enum_node;
07923    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
07924 
07925    data_queue = ast_data_add_node(data_root, "queue");
07926    if (!data_queue) {
07927       return;
07928    }
07929 
07930    ast_data_add_structure(call_queue, data_queue, queue);
07931 
07932    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
07933 
07934    /* announce position */
07935    enum_node = ast_data_add_node(data_queue, "announceposition");
07936    if (!enum_node) {
07937       return;
07938    }
07939    switch (queue->announceposition) {
07940    case ANNOUNCEPOSITION_LIMIT:
07941       ast_data_add_str(enum_node, "text", "limit");
07942       break;
07943    case ANNOUNCEPOSITION_MORE_THAN:
07944       ast_data_add_str(enum_node, "text", "more");
07945       break;
07946    case ANNOUNCEPOSITION_YES:
07947       ast_data_add_str(enum_node, "text", "yes");
07948       break;
07949    case ANNOUNCEPOSITION_NO:
07950       ast_data_add_str(enum_node, "text", "no");
07951       break;
07952    default:
07953       ast_data_add_str(enum_node, "text", "unknown");
07954       break;
07955    }
07956    ast_data_add_int(enum_node, "value", queue->announceposition);
07957 
07958    /* add queue members */
07959    im = ao2_iterator_init(queue->members, 0);
07960    while ((member = ao2_iterator_next(&im))) {
07961       if (!data_members) {
07962          data_members = ast_data_add_node(data_queue, "members");
07963          if (!data_members) {
07964             ao2_ref(member, -1);
07965             continue;
07966          }
07967       }
07968 
07969       data_member = ast_data_add_node(data_members, "member");
07970       if (!data_member) {
07971          ao2_ref(member, -1);
07972          continue;
07973       }
07974 
07975       ast_data_add_structure(member, data_member, member);
07976 
07977       ao2_ref(member, -1);
07978    }
07979 
07980    /* include the callers inside the result. */
07981    if (queue->head) {
07982       for (qe = queue->head; qe; qe = qe->next) {
07983          if (!data_callers) {
07984             data_callers = ast_data_add_node(data_queue, "callers");
07985             if (!data_callers) {
07986                continue;
07987             }
07988          }
07989 
07990          data_caller = ast_data_add_node(data_callers, "caller");
07991          if (!data_caller) {
07992             continue;
07993          }
07994 
07995          ast_data_add_structure(queue_ent, data_caller, qe);
07996 
07997          /* add the caller channel. */
07998          data_caller_channel = ast_data_add_node(data_caller, "channel");
07999          if (!data_caller_channel) {
08000             continue;
08001          }
08002 
08003          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08004       }
08005    }
08006 
08007    /* if this queue doesn't match remove the added queue. */
08008    if (!ast_data_search_match(search, data_queue)) {
08009       ast_data_remove_node(data_root, data_queue);
08010    }
08011 }

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

Definition at line 2576 of file app_queue.c.

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

02577 {
02578    int oldvalue;
02579 
02580    /* Calculate holdtime using an exponential average */
02581    /* Thanks to SRT for this contribution */
02582    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02583 
02584    ao2_lock(qe->parent);
02585    oldvalue = qe->parent->holdtime;
02586    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02587    ao2_unlock(qe->parent);
02588 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 3180 of file app_queue.c.

References ao2_lock, ao2_unlock, 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(), queue_ent::start, and ast_channel::uniqueid.

Referenced by queue_exec().

03181 {
03182    set_queue_variables(qe->parent, qe->chan);
03183    ao2_lock(qe->parent);
03184    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03185       "Queue: %s\r\n"
03186       "Uniqueid: %s\r\n"
03187       "Position: %d\r\n"
03188       "OriginalPosition: %d\r\n"
03189       "HoldTime: %d\r\n",
03190       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03191 
03192    qe->parent->callsabandoned++;
03193    ao2_unlock(qe->parent);
03194 }

static int reload ( void   )  [static]

Definition at line 8191 of file app_queue.c.

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

08192 {
08193    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08194    ast_unload_realtime("queue_members");
08195    reload_handler(1, &mask, NULL);
08196    return 0;
08197 }

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

06687 {
06688    int res = 0;
06689 
06690    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
06691       res |= reload_queue_rules(reload);
06692    }
06693    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
06694       res |= clear_stats(queuename);
06695    }
06696    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
06697       res |= reload_queues(reload, mask, queuename);
06698    }
06699    return res;
06700 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 5250 of file app_queue.c.

References add_to_queue(), ao2_lock, ao2_t_find, ao2_unlock, ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), ast_debug, ast_log(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, load_realtime_queue(), 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, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

05251 {
05252    char *cur_ptr;
05253    const char *queue_name;
05254    char *member;
05255    char *interface;
05256    char *membername = NULL;
05257    char *state_interface;
05258    char *penalty_tok;
05259    int penalty = 0;
05260    char *paused_tok;
05261    int paused = 0;
05262    struct ast_db_entry *db_tree;
05263    struct ast_db_entry *entry;
05264    struct call_queue *cur_queue;
05265    char queue_data[PM_MAX_LEN];
05266 
05267    ao2_lock(queues);
05268 
05269    /* Each key in 'pm_family' is the name of a queue */
05270    db_tree = ast_db_gettree(pm_family, NULL);
05271    for (entry = db_tree; entry; entry = entry->next) {
05272 
05273       queue_name = entry->key + strlen(pm_family) + 2;
05274 
05275       {
05276          struct call_queue tmpq = {
05277             .name = queue_name,
05278          };
05279          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05280       }  
05281 
05282       if (!cur_queue)
05283          cur_queue = load_realtime_queue(queue_name);
05284 
05285       if (!cur_queue) {
05286          /* If the queue no longer exists, remove it from the
05287           * database */
05288          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05289          ast_db_del(pm_family, queue_name);
05290          continue;
05291       } 
05292 
05293       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
05294          queue_t_unref(cur_queue, "Expire reload reference");
05295          continue;
05296       }
05297 
05298       cur_ptr = queue_data;
05299       while ((member = strsep(&cur_ptr, ",|"))) {
05300          if (ast_strlen_zero(member))
05301             continue;
05302 
05303          interface = strsep(&member, ";");
05304          penalty_tok = strsep(&member, ";");
05305          paused_tok = strsep(&member, ";");
05306          membername = strsep(&member, ";");
05307          state_interface = strsep(&member, ";");
05308 
05309          if (!penalty_tok) {
05310             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05311             break;
05312          }
05313          penalty = strtol(penalty_tok, NULL, 10);
05314          if (errno == ERANGE) {
05315             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05316             break;
05317          }
05318          
05319          if (!paused_tok) {
05320             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05321             break;
05322          }
05323          paused = strtol(paused_tok, NULL, 10);
05324          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05325             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05326             break;
05327          }
05328 
05329          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05330          
05331          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05332             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05333             break;
05334          }
05335       }
05336       queue_t_unref(cur_queue, "Expire reload reference");
05337    }
05338 
05339    ao2_unlock(queues);
05340    if (db_tree) {
05341       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05342       ast_db_freetree(db_tree);
05343    }
05344 }

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 6284 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_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, rule_list::rules, and ast_variable::value.

Referenced by reload_handler().

06285 {
06286    struct ast_config *cfg;
06287    struct rule_list *rl_iter, *new_rl;
06288    struct penalty_rule *pr_iter;
06289    char *rulecat = NULL;
06290    struct ast_variable *rulevar = NULL;
06291    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06292    
06293    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06294       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06295       return AST_MODULE_LOAD_SUCCESS;
06296    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06297       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06298       return AST_MODULE_LOAD_SUCCESS;
06299    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06300       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
06301       return AST_MODULE_LOAD_SUCCESS;
06302    }
06303 
06304    AST_LIST_LOCK(&rule_lists);
06305    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06306       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06307          ast_free(pr_iter);
06308       ast_free(rl_iter);
06309    }
06310    while ((rulecat = ast_category_browse(cfg, rulecat))) {
06311       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06312          AST_LIST_UNLOCK(&rule_lists);
06313          return AST_MODULE_LOAD_FAILURE;
06314       } else {
06315          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06316          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06317          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06318             if(!strcasecmp(rulevar->name, "penaltychange"))
06319                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06320             else
06321                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06322       }
06323    }
06324    AST_LIST_UNLOCK(&rule_lists);
06325 
06326    ast_config_destroy(cfg);
06327 
06328    return AST_MODULE_LOAD_SUCCESS;
06329 }

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 6598 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_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, kill_dead_queues(), LOG_ERROR, LOG_NOTICE, mark_dead_and_unfound(), OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, QUEUE_RELOAD_PARAMETERS, queue_set_global_params(), and reload_single_queue().

Referenced by reload_handler().

06599 {
06600    struct ast_config *cfg;
06601    char *cat;
06602    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06603    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06604 
06605    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
06606       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
06607       return -1;
06608    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06609       return 0;
06610    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06611       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
06612       return -1;
06613    }
06614 
06615    /* We've made it here, so it looks like we're doing operations on all queues. */
06616    ao2_lock(queues);
06617    
06618    /* Mark all queues as dead for the moment if we're reloading queues.
06619     * For clarity, we could just be reloading members, in which case we don't want to mess
06620     * with the other queue parameters at all*/
06621    if (queue_reload) {
06622       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
06623    }
06624 
06625    /* Chug through config file */
06626    cat = NULL;
06627    while ((cat = ast_category_browse(cfg, cat)) ) {
06628       if (!strcasecmp(cat, "general") && queue_reload) {
06629          queue_set_global_params(cfg);
06630          continue;
06631       }
06632       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
06633          reload_single_queue(cfg, mask, cat);
06634    }
06635 
06636    ast_config_destroy(cfg);
06637    /* Unref all the dead queues if we were reloading queues */
06638    if (queue_reload) {
06639       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
06640    }
06641    ao2_unlock(queues);
06642    return 0;
06643 }

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 6362 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::membercount, call_queue::members, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, and member::penalty.

Referenced by reload_single_queue().

06363 {
06364    char *membername, *interface, *state_interface, *tmp;
06365    char *parse;
06366    struct member *cur, *newm;
06367    struct member tmpmem;
06368    int penalty;
06369    AST_DECLARE_APP_ARGS(args,
06370       AST_APP_ARG(interface);
06371       AST_APP_ARG(penalty);
06372       AST_APP_ARG(membername);
06373       AST_APP_ARG(state_interface);
06374    );
06375 
06376    if (ast_strlen_zero(memberdata)) {
06377       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06378       return;
06379    }
06380 
06381    /* Add a new member */
06382    parse = ast_strdupa(memberdata);
06383             
06384    AST_STANDARD_APP_ARGS(args, parse);
06385 
06386    interface = args.interface;
06387    if (!ast_strlen_zero(args.penalty)) {
06388       tmp = args.penalty;
06389       ast_strip(tmp);
06390       penalty = atoi(tmp);
06391       if (penalty < 0) {
06392          penalty = 0;
06393       }
06394    } else {
06395       penalty = 0;
06396    }
06397 
06398    if (!ast_strlen_zero(args.membername)) {
06399       membername = args.membername;
06400       ast_strip(membername);
06401    } else {
06402       membername = interface;
06403    }
06404 
06405    if (!ast_strlen_zero(args.state_interface)) {
06406       state_interface = args.state_interface;
06407       ast_strip(state_interface);
06408    } else {
06409       state_interface = interface;
06410    }
06411 
06412    /* Find the old position in the list */
06413    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06414    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
06415    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06416       ao2_link(q->members, newm);
06417       ao2_ref(newm, -1);
06418    }
06419    newm = NULL;
06420 
06421    if (cur) {
06422       ao2_ref(cur, -1);
06423    } else {
06424       q->membercount++;
06425    }
06426 }

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

References alloc_queue(), ao2_callback, ao2_lock, ao2_t_find, ao2_unlock, ast_log(), ast_test_flag, ast_variable_browse(), ast_variable_retrieve(), call_queue::found, init_queue(), LOG_WARNING, mark_member_dead(), call_queue::membercount, call_queue::members, call_queue::name, OBJ_NODATA, OBJ_POINTER, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, reload_single_member(), strat2int(), call_queue::strategy, and var.

Referenced by reload_queues().

06469 {
06470    int new;
06471    struct call_queue *q = NULL;
06472    /*We're defining a queue*/
06473    struct call_queue tmpq = {
06474       .name = queuename,
06475    };
06476    const char *tmpvar;
06477    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06478    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
06479    int prev_weight = 0;
06480    struct ast_variable *var;
06481    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
06482       if (queue_reload) {
06483          /* Make one then */
06484          if (!(q = alloc_queue(queuename))) {
06485             return;
06486          }
06487       } else {
06488          /* Since we're not reloading queues, this means that we found a queue
06489           * in the configuration file which we don't know about yet. Just return.
06490           */
06491          return;
06492       }
06493       new = 1;
06494    } else {
06495       new = 0;
06496    }
06497    
06498    if (!new) {
06499       ao2_lock(q);
06500       prev_weight = q->weight ? 1 : 0;
06501    }
06502    /* Check if we already found a queue with this name in the config file */
06503    if (q->found) {
06504       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
06505       if (!new) {
06506          /* It should be impossible to *not* hit this case*/
06507          ao2_unlock(q);
06508       }
06509       queue_t_unref(q, "We exist! Expiring temporary pointer");
06510       return;
06511    }
06512    /* Due to the fact that the "linear" strategy will have a different allocation
06513     * scheme for queue members, we must devise the queue's strategy before other initializations.
06514     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
06515     * container used will have only a single bucket instead of the typical number.
06516     */
06517    if (queue_reload) {
06518       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
06519          q->strategy = strat2int(tmpvar);
06520          if (q->strategy < 0) {
06521             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
06522             tmpvar, q->name);
06523             q->strategy = QUEUE_STRATEGY_RINGALL;
06524          }
06525       } else {
06526          q->strategy = QUEUE_STRATEGY_RINGALL;
06527       }
06528       init_queue(q);
06529    }
06530    if (member_reload) {
06531       q->membercount = 0;
06532       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
06533    }
06534    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
06535       if (member_reload && !strcasecmp(var->name, "member")) {
06536          reload_single_member(var->value, q);
06537       } else if (queue_reload) {
06538          queue_set_param(q, var->name, var->value, var->lineno, 1);
06539       }
06540    }
06541    /* At this point, we've determined if the queue has a weight, so update use_weight
06542     * as appropriate
06543     */
06544    if (!q->weight && prev_weight) {
06545       ast_atomic_fetchadd_int(&use_weight, -1);
06546    }
06547    else if (q->weight && !prev_weight) {
06548       ast_atomic_fetchadd_int(&use_weight, +1);
06549    }
06550 
06551    /* Free remaining members marked as delme */
06552    if (member_reload) {
06553       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
06554    }
06555 
06556    if (new) {
06557       queues_t_link(queues, q, "Add queue to container");
06558    } else {
06559       ao2_unlock(q);
06560    }
06561    queue_t_unref(q, "Expiring creation reference");
06562 }

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

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

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

04986 {
04987    struct call_queue *q, tmpq = {
04988       .name = queuename,   
04989    };
04990    struct member *mem, tmpmem;
04991    int res = RES_NOSUCHQUEUE;
04992 
04993    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04994    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
04995       ao2_lock(queues);
04996       ao2_lock(q);
04997       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04998          /* XXX future changes should beware of this assumption!! */
04999          if (!mem->dynamic) {
05000             ao2_ref(mem, -1);
05001             ao2_unlock(q);
05002             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05003             ao2_unlock(queues);
05004             return RES_NOT_DYNAMIC;
05005          }
05006          q->membercount--;
05007          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05008             "Queue: %s\r\n"
05009             "Location: %s\r\n"
05010             "MemberName: %s\r\n",
05011             q->name, mem->interface, mem->membername);
05012          ao2_unlink(q->members, mem);
05013          ao2_ref(mem, -1);
05014 
05015          if (queue_persistent_members)
05016             dump_queue_members(q);
05017          
05018          res = RES_OKAY;
05019       } else {
05020          res = RES_EXISTS;
05021       }
05022       ao2_unlock(q);
05023       ao2_unlock(queues);
05024       queue_t_unref(q, "Expiring temporary reference");
05025    }
05026 
05027    return res;
05028 }

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:

Return values:
1 on success to reach a free agent
0 on failure to get agent.

Definition at line 2839 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, ast_channel_set_caller_event(), ast_channel_trylock, ast_channel_unlock, ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, AST_FLAG_ANSWERED_ELSEWHERE, ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_set_flag, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, CHANNEL_DEADLOCK_AVOIDANCE, ast_cdr::clid, compare_weight(), ast_channel::connected, ast_channel::context, ast_channel::data, ast_cdr::dcontext, callattempt::dial_callerid_absent, dialcontext, ast_channel::dialed, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, get_queue_member_status(), ast_party_connected_line::id, ast_party_caller::id, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, ast_channel::macroexten, manager_event, callattempt::member, member::membername, ast_party_id::name, ast_channel::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, ast_channel::uniqueid, 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().

02840 {
02841    int res;
02842    int status;
02843    char tech[256];
02844    char *location;
02845    const char *macrocontext, *macroexten;
02846 
02847    /* on entry here, we know that tmp->chan == NULL */
02848    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02849       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02850       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
02851             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02852       if (qe->chan->cdr)
02853          ast_cdr_busy(qe->chan->cdr);
02854       tmp->stillgoing = 0;
02855       (*busies)++;
02856       return 0;
02857    }
02858 
02859    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02860       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02861       if (qe->chan->cdr)
02862          ast_cdr_busy(qe->chan->cdr);
02863       tmp->stillgoing = 0;
02864       return 0;
02865    }
02866 
02867    if (tmp->member->paused) {
02868       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02869       if (qe->chan->cdr)
02870          ast_cdr_busy(qe->chan->cdr);
02871       tmp->stillgoing = 0;
02872       return 0;
02873    }
02874    if (use_weight && compare_weight(qe->parent,tmp->member)) {
02875       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02876       if (qe->chan->cdr)
02877          ast_cdr_busy(qe->chan->cdr);
02878       tmp->stillgoing = 0;
02879       (*busies)++;
02880       return 0;
02881    }
02882 
02883    ast_copy_string(tech, tmp->interface, sizeof(tech));
02884    if ((location = strchr(tech, '/')))
02885       *location++ = '\0';
02886    else
02887       location = "";
02888 
02889    /* Request the peer */
02890    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
02891    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02892       if (qe->chan->cdr)
02893          ast_cdr_busy(qe->chan->cdr);
02894       tmp->stillgoing = 0; 
02895 
02896       ao2_lock(qe->parent);
02897       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
02898       qe->parent->rrpos++;
02899       qe->linpos++;
02900       ao2_unlock(qe->parent);
02901 
02902       (*busies)++;
02903       return 0;
02904    }
02905 
02906    ast_channel_lock(tmp->chan);
02907    while (ast_channel_trylock(qe->chan)) {
02908       CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan);
02909    }
02910 
02911    if (qe->cancel_answered_elsewhere) {
02912       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02913    }
02914    tmp->chan->appl = "AppQueue";
02915    tmp->chan->data = "(Outgoing Line)";
02916    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02917 
02918    /* If the new channel has no callerid, try to guess what it should be */
02919    if (!tmp->chan->caller.id.number.valid) {
02920       if (qe->chan->connected.id.number.valid) {
02921          struct ast_party_caller caller;
02922 
02923          ast_party_caller_set_init(&caller, &tmp->chan->caller);
02924          caller.id = qe->chan->connected.id;
02925          caller.ani = qe->chan->connected.ani;
02926          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
02927       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
02928          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
02929       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
02930          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
02931       }
02932       tmp->dial_callerid_absent = 1;
02933    }
02934 
02935    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
02936 
02937    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
02938 
02939    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
02940 
02941    /* Inherit specially named variables from parent channel */
02942    ast_channel_inherit_variables(qe->chan, tmp->chan);
02943    ast_channel_datastore_inherit(qe->chan, tmp->chan);
02944 
02945    /* Presense of ADSI CPE on outgoing channel follows ours */
02946    tmp->chan->adsicpe = qe->chan->adsicpe;
02947 
02948    /* Inherit context and extension */
02949    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02950    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02951    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02952    if (!ast_strlen_zero(macroexten))
02953       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02954    else
02955       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02956    if (ast_cdr_isset_unanswered()) {
02957       /* they want to see the unanswered dial attempts! */
02958       /* set up the CDR fields on all the CDRs to give sensical information */
02959       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02960       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02961       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02962       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02963       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02964       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02965       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02966       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02967       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02968       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02969       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02970    }
02971 
02972    /* Place the call, but don't wait on the answer */
02973    if ((res = ast_call(tmp->chan, location, 0))) {
02974       /* Again, keep going even if there's an error */
02975       ast_debug(1, "ast call on peer returned %d\n", res);
02976       ast_verb(3, "Couldn't call %s\n", tmp->interface);
02977       ast_channel_unlock(tmp->chan);
02978       ast_channel_unlock(qe->chan);
02979       do_hang(tmp);
02980       (*busies)++;
02981       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
02982       return 0;
02983    } else if (qe->parent->eventwhencalled) {
02984       char vars[2048];
02985 
02986       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02987          "Queue: %s\r\n"
02988          "AgentCalled: %s\r\n"
02989          "AgentName: %s\r\n"
02990          "ChannelCalling: %s\r\n"
02991          "DestinationChannel: %s\r\n"
02992          "CallerIDNum: %s\r\n"
02993          "CallerIDName: %s\r\n"
02994          "Context: %s\r\n"
02995          "Extension: %s\r\n"
02996          "Priority: %d\r\n"
02997          "Uniqueid: %s\r\n"
02998          "%s",
02999          qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03000          S_COR(tmp->chan->caller.id.number.valid, tmp->chan->caller.id.number.str, "unknown"),
03001          S_COR(tmp->chan->caller.id.name.valid, tmp->chan->caller.id.name.str, "unknown"),
03002          qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03003          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03004       ast_verb(3, "Called %s\n", tmp->interface);
03005    }
03006    ast_channel_unlock(tmp->chan);
03007    ast_channel_unlock(qe->chan);
03008 
03009    update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03010    return 1;
03011 }

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

03040 {
03041    int ret = 0;
03042 
03043    while (ret == 0) {
03044       struct callattempt *best = find_best(outgoing);
03045       if (!best) {
03046          ast_debug(1, "Nobody left to try ringing in queue\n");
03047          break;
03048       }
03049       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03050          struct callattempt *cur;
03051          /* Ring everyone who shares this best metric (for ringall) */
03052          for (cur = outgoing; cur; cur = cur->q_next) {
03053             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03054                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03055                ret |= ring_entry(qe, cur, busies);
03056             }
03057          }
03058       } else {
03059          /* Ring just the best channel */
03060          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03061          ret = ring_entry(qe, best, busies);
03062       }
03063       
03064       /* If we have timed out, break out */
03065       if (qe->expire && (time(NULL) >= qe->expire)) {
03066          ast_debug(1, "Queue timed out while ringing members.\n");
03067          ret = 0;
03068          break;
03069       }
03070    }
03071 
03072    return ret;
03073 }

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

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

03198 {
03199    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03200 
03201    /* Stop ringing, and resume MOH if specified */
03202    if (qe->ring_when_ringing) {
03203       ast_indicate(qe->chan, -1);
03204       ast_moh_start(qe->chan, qe->moh, NULL);
03205    }
03206 
03207    if (qe->parent->eventwhencalled) {
03208       char vars[2048];
03209 
03210       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03211                   "Queue: %s\r\n"
03212                   "Uniqueid: %s\r\n"
03213                   "Channel: %s\r\n"
03214                   "Member: %s\r\n"
03215                   "MemberName: %s\r\n"
03216                   "Ringtime: %d\r\n"
03217                   "%s",
03218                   qe->parent->name,
03219                   qe->chan->uniqueid,
03220                   qe->chan->name,
03221                   interface,
03222                   membername,
03223                   rnatime,
03224                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03225    }
03226    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03227    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03228       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03229          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03230             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03231                interface, qe->parent->name);
03232          } else {
03233             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03234          }
03235       } else {
03236          /* If queue autopause is mode all, just don't send any queue to stop.
03237          * the function will stop in all queues */
03238          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03239             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03240                   interface, qe->parent->name);
03241          } else {
03242                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03243          }
03244       }
03245    }
03246    return;
03247 }

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

RemoveQueueMember application.

Definition at line 5419 of file app_queue.c.

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

Referenced by load_module().

05420 {
05421    int res=-1;
05422    char *parse, *temppos = NULL;
05423    AST_DECLARE_APP_ARGS(args,
05424       AST_APP_ARG(queuename);
05425       AST_APP_ARG(interface);
05426       AST_APP_ARG(options);
05427    );
05428 
05429 
05430    if (ast_strlen_zero(data)) {
05431       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
05432       return -1;
05433    }
05434 
05435    parse = ast_strdupa(data);
05436 
05437    AST_STANDARD_APP_ARGS(args, parse);
05438 
05439    if (ast_strlen_zero(args.interface)) {
05440       args.interface = ast_strdupa(chan->name);
05441       temppos = strrchr(args.interface, '-');
05442       if (temppos)
05443          *temppos = '\0';
05444    }
05445 
05446    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05447 
05448    switch (remove_from_queue(args.queuename, args.interface)) {
05449    case RES_OKAY:
05450       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05451       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05452       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05453       res = 0;
05454       break;
05455    case RES_EXISTS:
05456       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05457       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05458       res = 0;
05459       break;
05460    case RES_NOSUCHQUEUE:
05461       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05462       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05463       res = 0;
05464       break;
05465    case RES_NOT_DYNAMIC:
05466       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05467       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05468       res = 0;
05469       break;
05470    }
05471 
05472    return res;
05473 }

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

Find rt member record to update otherwise create one.

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

Definition at line 1916 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(), create_queue_member(), member::dead, member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::rt_uniqueid, S_OR, and member::state_interface.

Referenced by update_realtime_members().

01917 {
01918    struct member *m;
01919    struct ao2_iterator mem_iter;
01920    int penalty = 0;
01921    int paused  = 0;
01922    int found = 0;
01923 
01924    if (ast_strlen_zero(rt_uniqueid)) {
01925       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
01926       return;
01927    }
01928 
01929    if (penalty_str) {
01930       penalty = atoi(penalty_str);
01931       if (penalty < 0)
01932          penalty = 0;
01933    }
01934 
01935    if (paused_str) {
01936       paused = atoi(paused_str);
01937       if (paused < 0)
01938          paused = 0;
01939    }
01940 
01941    /* Find member by realtime uniqueid and update */
01942    mem_iter = ao2_iterator_init(q->members, 0);
01943    while ((m = ao2_iterator_next(&mem_iter))) {
01944       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01945          m->dead = 0;   /* Do not delete this one. */
01946          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01947          if (paused_str)
01948             m->paused = paused;
01949          if (strcasecmp(state_interface, m->state_interface)) {
01950             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01951          }     
01952          m->penalty = penalty;
01953          found = 1;
01954          ao2_ref(m, -1);
01955          break;
01956       }
01957       ao2_ref(m, -1);
01958    }
01959    ao2_iterator_destroy(&mem_iter);
01960 
01961    /* Create a new member */
01962    if (!found) {
01963       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01964          m->dead = 0;
01965          m->realtime = 1;
01966          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01967          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01968          ao2_link(q->members, m);
01969          ao2_ref(m, -1);
01970          m = NULL;
01971          q->membercount++;
01972       }
01973    }
01974 }

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

Playback announcement to queued members if peroid has elapsed.

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

03125 {
03126    int res = 0;
03127    time_t now;
03128 
03129    /* Get the current time */
03130    time(&now);
03131 
03132    /* Check to see if it is time to announce */
03133    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03134       return 0;
03135 
03136    /* Stop the music on hold so we can play our own file */
03137    if (ringing)
03138       ast_indicate(qe->chan,-1);
03139    else
03140       ast_moh_stop(qe->chan);
03141 
03142    ast_verb(3, "Playing periodic announcement\n");
03143    
03144    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03145       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03146    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03147       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03148       qe->last_periodic_announce_sound = 0;
03149    }
03150    
03151    /* play the announcement */
03152    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03153 
03154    if (res > 0 && !valid_exit(qe, res))
03155       res = 0;
03156 
03157    /* Resume Music on Hold if the caller is going to stay in the queue */
03158    if (!res) {
03159       if (ringing)
03160          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03161       else
03162          ast_moh_start(qe->chan, qe->moh, NULL);
03163    }
03164 
03165    /* update last_periodic_announce_time */
03166    if (qe->parent->relativeperiodicannounce)
03167       time(&qe->last_periodic_announce_time);
03168    else
03169       qe->last_periodic_announce_time = now;
03170 
03171    /* Update the current periodic announcement to the next announcement */
03172    if (!qe->parent->randomperiodicannounce) {
03173       qe->last_periodic_announce_sound++;
03174    }
03175    
03176    return res;
03177 }

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

Definition at line 2432 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, AST_CONTROL_RINGING, AST_DIGIT_ANY, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verb, queue_ent::chan, call_queue::holdtime, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, ast_channel::name, 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().

02433 {
02434    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02435    int say_thanks = 1;
02436    time_t now;
02437 
02438    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02439    time(&now);
02440    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02441       return 0;
02442 
02443    /* If either our position has changed, or we are over the freq timer, say position */
02444    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02445       return 0;
02446 
02447    if (ringing) {
02448       ast_indicate(qe->chan,-1);
02449    } else {
02450       ast_moh_stop(qe->chan);
02451    }
02452 
02453    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02454       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02455       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02456       qe->pos <= qe->parent->announcepositionlimit))
02457          announceposition = 1;
02458 
02459 
02460    if (announceposition == 1) {
02461       /* Say we're next, if we are */
02462       if (qe->pos == 1) {
02463          res = play_file(qe->chan, qe->parent->sound_next);
02464          if (res)
02465             goto playout;
02466          else
02467             goto posout;
02468       } else {
02469          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02470             /* More than Case*/
02471             res = play_file(qe->chan, qe->parent->queue_quantity1);
02472             if (res)
02473                goto playout;
02474             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02475             if (res)
02476                goto playout;
02477          } else {
02478             /* Normal Case */
02479             res = play_file(qe->chan, qe->parent->sound_thereare);
02480             if (res)
02481                goto playout;
02482             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02483             if (res)
02484                goto playout;
02485          }
02486          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02487             /* More than Case*/
02488             res = play_file(qe->chan, qe->parent->queue_quantity2);
02489             if (res)
02490                goto playout;
02491          } else {
02492             res = play_file(qe->chan, qe->parent->sound_calls);
02493             if (res)
02494                goto playout;
02495          }
02496       }
02497    }
02498    /* Round hold time to nearest minute */
02499    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02500 
02501    /* If they have specified a rounding then round the seconds as well */
02502    if (qe->parent->roundingseconds) {
02503       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02504       avgholdsecs *= qe->parent->roundingseconds;
02505    } else {
02506       avgholdsecs = 0;
02507    }
02508 
02509    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02510 
02511    /* If the hold time is >1 min, if it's enabled, and if it's not
02512       supposed to be only once and we have already said it, say it */
02513     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02514         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02515         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02516       res = play_file(qe->chan, qe->parent->sound_holdtime);
02517       if (res)
02518          goto playout;
02519 
02520       if (avgholdmins >= 1) {
02521          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02522          if (res)
02523             goto playout;
02524 
02525          if (avgholdmins == 1) {
02526             res = play_file(qe->chan, qe->parent->sound_minute);
02527             if (res)
02528                goto playout;
02529          } else {
02530             res = play_file(qe->chan, qe->parent->sound_minutes);
02531             if (res)
02532                goto playout;
02533          }
02534       }
02535       if (avgholdsecs >= 1) {
02536          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02537          if (res)
02538             goto playout;
02539 
02540          res = play_file(qe->chan, qe->parent->sound_seconds);
02541          if (res)
02542             goto playout;
02543       }
02544    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02545       say_thanks = 0;
02546    }
02547 
02548 posout:
02549    if (qe->parent->announceposition) {
02550       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02551          qe->chan->name, qe->parent->name, qe->pos);
02552    }
02553    if (say_thanks) {
02554       res = play_file(qe->chan, qe->parent->sound_thanks);
02555    }
02556 playout:
02557 
02558    if ((res > 0 && !valid_exit(qe, res)))
02559       res = 0;
02560 
02561    /* Set our last_pos indicators */
02562    qe->last_pos = now;
02563    qe->last_pos_said = qe->pos;
02564 
02565    /* Don't restart music on hold if we're about to exit the caller from the queue */
02566    if (!res) {
02567       if (ringing) {
02568          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02569       } else {
02570          ast_moh_start(qe->chan, qe->moh, NULL);
02571       }
02572    }
02573    return res;
02574 }

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

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

03957 {
03958    const char *reason = NULL; /* silence dumb compilers */
03959 
03960    if (!qe->parent->eventwhencalled)
03961       return;
03962 
03963    switch (rsn) {
03964    case CALLER:
03965       reason = "caller";
03966       break;
03967    case AGENT:
03968       reason = "agent";
03969       break;
03970    case TRANSFER:
03971       reason = "transfer";
03972       break;
03973    }
03974 
03975    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03976       "Queue: %s\r\n"
03977       "Uniqueid: %s\r\n"
03978       "Channel: %s\r\n"
03979       "Member: %s\r\n"
03980       "MemberName: %s\r\n"
03981       "HoldTime: %ld\r\n"
03982       "TalkTime: %ld\r\n"
03983       "Reason: %s\r\n"
03984       "%s",
03985       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03986       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03987       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03988 }

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

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

05093 {
05094    int found = 0;
05095    struct call_queue *q;
05096    struct member *mem;
05097    struct ao2_iterator queue_iter;
05098    int failed;
05099 
05100    /* Special event for when all queues are paused - individual events still generated */
05101    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05102    if (ast_strlen_zero(queuename))
05103       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05104 
05105    queue_iter = ao2_iterator_init(queues, 0);
05106    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05107       ao2_lock(q);
05108       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05109          if ((mem = interface_exists(q, interface))) {
05110             if (mem->paused == paused) {
05111                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05112             }
05113 
05114             failed = 0;
05115             if (mem->realtime) {
05116                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05117             }
05118          
05119             if (failed) {
05120                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05121                ao2_ref(mem, -1);
05122                ao2_unlock(q);
05123                queue_t_unref(q, "Done with iterator");
05124                continue;
05125             }  
05126             found++;
05127             mem->paused = paused;
05128 
05129             if (queue_persistent_members)
05130                dump_queue_members(q);
05131 
05132             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05133             
05134             if (!ast_strlen_zero(reason)) {
05135                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05136                   "Queue: %s\r\n"
05137                   "Location: %s\r\n"
05138                   "MemberName: %s\r\n"
05139                   "Paused: %d\r\n"
05140                   "Reason: %s\r\n",
05141                      q->name, mem->interface, mem->membername, paused, reason);
05142             } else {
05143                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05144                   "Queue: %s\r\n"
05145                   "Location: %s\r\n"
05146                   "MemberName: %s\r\n"
05147                   "Paused: %d\r\n",
05148                      q->name, mem->interface, mem->membername, paused);
05149             }
05150             ao2_ref(mem, -1);
05151          }
05152       }
05153       
05154       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05155          ao2_unlock(q);
05156          queue_t_unref(q, "Done with iterator");
05157          break;
05158       }
05159       
05160       ao2_unlock(q);
05161       queue_t_unref(q, "Done with iterator");
05162    }
05163    ao2_iterator_destroy(&queue_iter);
05164 
05165    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05166 }

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

Definition at line 5169 of file app_queue.c.

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

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

05170 {
05171    int foundinterface = 0, foundqueue = 0;
05172    struct call_queue *q;
05173    struct member *mem;
05174    struct ao2_iterator queue_iter;
05175 
05176    if (penalty < 0) {
05177       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05178       return RESULT_FAILURE;
05179    }
05180 
05181    queue_iter = ao2_iterator_init(queues, 0);
05182    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05183       ao2_lock(q);
05184       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05185          foundqueue++;
05186          if ((mem = interface_exists(q, interface))) {
05187             foundinterface++;
05188             mem->penalty = penalty;
05189             
05190             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05191             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05192                "Queue: %s\r\n"
05193                "Location: %s\r\n"
05194                "Penalty: %d\r\n",
05195                q->name, mem->interface, penalty);
05196             ao2_ref(mem, -1);
05197          }
05198       }
05199       ao2_unlock(q);
05200       queue_t_unref(q, "Done with iterator");
05201    }
05202    ao2_iterator_destroy(&queue_iter);
05203 
05204    if (foundinterface) {
05205       return RESULT_SUCCESS;
05206    } else if (!foundqueue) {
05207       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05208    } else {
05209       ast_log (LOG_ERROR, "Invalid interface\n");
05210    }  
05211 
05212    return RESULT_FAILURE;
05213 }

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

sets the QUEUESTATUS channel variable

Definition at line 1037 of file app_queue.c.

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

Referenced by queue_exec().

01038 {
01039    int i;
01040 
01041    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
01042       if (queue_results[i].id == res) {
01043          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
01044          return;
01045       }
01046    }
01047 }

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

Set variables of queue.

Definition at line 1132 of file app_queue.c.

References ao2_lock, ao2_unlock, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, 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(), and record_abandoned().

01133 {
01134    char interfacevar[256]="";
01135    float sl = 0;
01136 
01137    ao2_lock(q);
01138 
01139    if (q->setqueuevar) {
01140       sl = 0;
01141       if (q->callscompleted > 0) 
01142          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01143 
01144       snprintf(interfacevar, sizeof(interfacevar),
01145          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01146          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01147 
01148       ao2_unlock(q);
01149    
01150       pbx_builtin_setvar_multiple(chan, interfacevar); 
01151    } else {
01152       ao2_unlock(q);
01153    }
01154 }

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

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

Definition at line 4058 of file app_queue.c.

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

04059 {
04060    struct ast_datastore *ds;
04061    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04062 
04063    if (!qtds) {
04064       ast_log(LOG_WARNING, "Memory allocation error!\n");
04065       return NULL;
04066    }
04067 
04068    ast_channel_lock(qe->chan);
04069    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04070       ast_channel_unlock(qe->chan);
04071       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04072       return NULL;
04073    }
04074 
04075    qtds->qe = qe;
04076    /* This member is refcounted in try_calling, so no need to add it here, too */
04077    qtds->member = member;
04078    qtds->starttime = starttime;
04079    qtds->callcompletedinsl = callcompletedinsl;
04080    ds->data = qtds;
04081    ast_channel_datastore_add(qe->chan, ds);
04082    ast_channel_unlock(qe->chan);
04083    return ds;
04084 }

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

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

03101 {
03102    struct callattempt *best = find_best(outgoing);
03103 
03104    if (best) {
03105       /* Ring just the best channel */
03106       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03107       qe->linpos = best->metric % 1000;
03108    } else {
03109       /* Just increment rrpos */
03110       if (qe->linwrapped) {
03111          /* No more channels, start over */
03112          qe->linpos = 0;
03113       } else {
03114          /* Prioritize next entry */
03115          qe->linpos++;
03116       }
03117    }
03118    qe->linwrapped = 0;
03119 
03120    return 0;
03121 }

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

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

03077 {
03078    struct callattempt *best = find_best(outgoing);
03079 
03080    if (best) {
03081       /* Ring just the best channel */
03082       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03083       qe->parent->rrpos = best->metric % 1000;
03084    } else {
03085       /* Just increment rrpos */
03086       if (qe->parent->wrapped) {
03087          /* No more channels, start over */
03088          qe->parent->rrpos = 0;
03089       } else {
03090          /* Prioritize next entry */
03091          qe->parent->rrpos++;
03092       }
03093    }
03094    qe->parent->wrapped = 0;
03095 
03096    return 0;
03097 }

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

Definition at line 1061 of file app_queue.c.

References ARRAY_LEN, strategies, and strategy::strategy.

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

01062 {
01063    int x;
01064 
01065    for (x = 0; x < ARRAY_LEN(strategies); x++) {
01066       if (!strcasecmp(strategy, strategies[x].name))
01067          return strategies[x].strategy;
01068    }
01069 
01070    return -1;
01071 }

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

References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, 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_MAX_CONTEXT, AST_MAX_EXTENSION, ast_set_flag, ast_test_flag, queue_ent::cancel_answered_elsewhere, queue_ent::chan, dialed_interface_info, queue_ent::expire, call_queue::membercount, queue_ent::parent, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, and call_queue::strategy.

Referenced by queue_exec().

04139 {
04140    struct member *cur;
04141    struct callattempt *outgoing = NULL; /* the list of calls we are building */
04142    int to, orig;
04143    char oldexten[AST_MAX_EXTENSION]="";
04144    char oldcontext[AST_MAX_CONTEXT]="";
04145    char queuename[256]="";
04146    char interfacevar[256]="";
04147    struct ast_channel *peer;
04148    struct ast_channel *which;
04149    struct callattempt *lpeer;
04150    struct member *member;
04151    struct ast_app *application;
04152    int res = 0, bridge = 0;
04153    int numbusies = 0;
04154    int x=0;
04155    char *announce = NULL;
04156    char digit = 0;
04157    time_t callstart;
04158    time_t now = time(NULL);
04159    struct ast_bridge_config bridge_config;
04160    char nondataquality = 1;
04161    char *agiexec = NULL;
04162    char *macroexec = NULL;
04163    char *gosubexec = NULL;
04164    int ret = 0;
04165    const char *monitorfilename;
04166    const char *monitor_exec;
04167    const char *monitor_options;
04168    char tmpid[256], tmpid2[256];
04169    char meid[1024], meid2[1024];
04170    char mixmonargs[1512];
04171    struct ast_app *mixmonapp = NULL;
04172    char *p;
04173    char vars[2048];
04174    int forwardsallowed = 1;
04175    int update_connectedline = 1;
04176    int callcompletedinsl;
04177    struct ao2_iterator memi;
04178    struct ast_datastore *datastore, *transfer_ds;
04179    struct queue_end_bridge *queue_end_bridge = NULL;
04180    const int need_weight = use_weight;
04181 
04182    ast_channel_lock(qe->chan);
04183    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
04184    ast_channel_unlock(qe->chan);
04185 
04186    memset(&bridge_config, 0, sizeof(bridge_config));
04187    tmpid[0] = 0;
04188    meid[0] = 0;
04189    time(&now);
04190 
04191    /* If we've already exceeded our timeout, then just stop
04192     * This should be extremely rare. queue_exec will take care
04193     * of removing the caller and reporting the timeout as the reason.
04194     */
04195    if (qe->expire && now >= qe->expire) {
04196       res = 0;
04197       goto out;
04198    }
04199       
04200    for (; options && *options; options++)
04201       switch (*options) {
04202       case 't':
04203          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
04204          break;
04205       case 'T':
04206          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
04207          break;
04208       case 'w':
04209          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
04210          break;
04211       case 'W':
04212          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
04213          break;
04214       case 'c':
04215          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
04216          break;
04217       case 'd':
04218          nondataquality = 0;
04219          break;
04220       case 'h':
04221          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
04222          break;
04223       case 'H':
04224          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
04225          break;
04226       case 'k':
04227          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
04228          break;
04229       case 'K':
04230          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
04231          break;
04232       case 'n':
04233          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
04234             (*tries)++;
04235          else
04236             *tries = qe->parent->membercount;
04237          *noption = 1;
04238          break;
04239       case 'i':
04240          forwardsallowed = 0;
04241          break;
04242       case 'I':
04243          update_connectedline = 0;
04244          break;
04245       case 'x':
04246          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
04247          break;
04248       case 'X':
04249          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
04250          break;
04251       case 'C':
04252          qe->cancel_answered_elsewhere = 1;
04253          break;
04254       }
04255 
04256    /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 
04257       (this is mainly to support chan_local)
04258    */
04259    if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) {
04260       qe->cancel_answered_elsewhere = 1;
04261    }
04262 
04263    /* Hold the lock while we setup the outgoing calls */
04264    if (need_weight)
04265       ao2_lock(queues);
04266    ao2_lock(qe->parent);
04267    ast_debug(1, "%s is trying to call a queue member.\n",
04268                      qe->chan->name);
04269    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
04270    if (!ast_strlen_zero(qe->announce))
04271       announce = qe->announce;
04272    if (!ast_strlen_zero(announceoverride))
04273       announce = announceoverride;
04274 
04275    memi = ao2_iterator_init(qe->parent->members, 0);
04276    while ((cur = ao2_iterator_next(&memi))) {
04277       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
04278       struct ast_dialed_interface *di;
04279       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
04280       if (!tmp) {
04281          ao2_ref(cur, -1);
04282          ao2_unlock(qe->parent);
04283          ao2_iterator_destroy(&memi);
04284          if (need_weight)
04285             ao2_unlock(queues);
04286          goto out;
04287       }
04288       if (!datastore) {
04289          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
04290             ao2_ref(cur, -1);
04291             ao2_unlock(qe->parent);
04292             ao2_iterator_destroy(&memi);
04293             if (need_weight)
04294                ao2_unlock(queues);
04295             callattempt_free(tmp);
04296             goto out;
04297          }
04298          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
04299          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
04300             ao2_ref(cur, -1);
04301             ao2_unlock(&qe->parent);
04302             ao2_iterator_destroy(&memi);
04303             if (need_weight)
04304                ao2_unlock(queues);
04305             callattempt_free(tmp);
04306             goto out;
04307          }
04308          datastore->data = dialed_interfaces;
04309          AST_LIST_HEAD_INIT(dialed_interfaces);
04310 
04311          ast_channel_lock(qe->chan);
04312          ast_channel_datastore_add(qe->chan, datastore);
04313          ast_channel_unlock(qe->chan);
04314       } else
04315          dialed_interfaces = datastore->data;
04316 
04317       AST_LIST_LOCK(dialed_interfaces);
04318       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
04319          if (!strcasecmp(cur->interface, di->interface)) {
04320             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
04321                di->interface);
04322             break;
04323          }
04324       }
04325       AST_LIST_UNLOCK(dialed_interfaces);
04326 
04327       if (di) {
04328          callattempt_free(tmp);
04329          continue;
04330       }
04331 
04332       /* It is always ok to dial a Local interface.  We only keep track of
04333        * which "real" interfaces have been dialed.  The Local channel will
04334        * inherit this list so that if it ends up dialing a real interface,
04335        * it won't call one that has already been called. */
04336       if (strncasecmp(cur->interface, "Local/", 6)) {
04337          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
04338             ao2_ref(cur, -1);
04339             ao2_unlock(qe->parent);
04340             ao2_iterator_destroy(&memi);
04341             if (need_weight)
04342                ao2_unlock(queues);
04343             callattempt_free(tmp);
04344             goto out;
04345          }
04346          strcpy(di->interface, cur->interface);
04347 
04348          AST_LIST_LOCK(dialed_interfaces);
04349          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
04350          AST_LIST_UNLOCK(dialed_interfaces);
04351       }
04352 
04353       ast_channel_lock(qe->chan);
04354       /*
04355        * Seed the callattempt's connected line information with previously
04356        * acquired connected line info from the queued channel.  The
04357        * previously acquired connected line info could have been set
04358        * through the CONNECTED_LINE dialplan function.
04359        */
04360       ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected);
04361       ast_channel_unlock(qe->chan);
04362 
04363       tmp->stillgoing = -1;
04364       tmp->member = cur;/* Place the reference for cur into callattempt. */
04365       tmp->lastcall = cur->lastcall;
04366       tmp->lastqueue = cur->lastqueue;
04367       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
04368       /* Special case: If we ring everyone, go ahead and ring them, otherwise
04369          just calculate their metric for the appropriate strategy */
04370       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
04371          /* Put them in the list of outgoing thingies...  We're ready now.
04372             XXX If we're forcibly removed, these outgoing calls won't get
04373             hung up XXX */
04374          tmp->q_next = outgoing;
04375          outgoing = tmp;      
04376          /* If this line is up, don't try anybody else */
04377          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
04378             break;
04379       } else {
04380          callattempt_free(tmp);
04381       }
04382    }
04383    ao2_iterator_destroy(&memi);
04384 
04385    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
04386       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
04387       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
04388          to = (qe->expire - now) * 1000;
04389       else
04390          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
04391    } else {
04392       /* Config timeout is higher priority thatn application timeout */
04393       if (qe->expire && qe->expire<=now) {
04394          to = 0;
04395       } else if (qe->parent->timeout) {
04396          to = qe->parent->timeout * 1000;
04397       } else {
04398          to = -1;
04399       }
04400    }
04401    orig = to;
04402    ++qe->pending;
04403    ao2_unlock(qe->parent);
04404    ring_one(qe, outgoing, &numbusies);
04405    if (need_weight)
04406       ao2_unlock(queues);
04407    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline);
04408    /* The ast_channel_datastore_remove() function could fail here if the
04409     * datastore was moved to another channel during a masquerade. If this is
04410     * the case, don't free the datastore here because later, when the channel
04411     * to which the datastore was moved hangs up, it will attempt to free this
04412     * datastore again, causing a crash
04413     */
04414    ast_channel_lock(qe->chan);
04415    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
04416       ast_datastore_free(datastore);
04417    }
04418    ast_channel_unlock(qe->chan);
04419    ao2_lock(qe->parent);
04420    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
04421       store_next_rr(qe, outgoing);
04422    }
04423    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
04424       store_next_lin(qe, outgoing);
04425    }
04426    ao2_unlock(qe->parent);
04427    peer = lpeer ? lpeer->chan : NULL;
04428    if (!peer) {
04429       qe->pending = 0;
04430       if (to) {
04431          /* Must gotten hung up */
04432          res = -1;
04433       } else {
04434          /* User exited by pressing a digit */
04435          res = digit;
04436       }
04437       if (res == -1)
04438          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
04439       if (ast_cdr_isset_unanswered()) {
04440          /* channel contains the name of one of the outgoing channels
04441             in its CDR; zero out this CDR to avoid a dual-posting */
04442          struct callattempt *o;
04443          for (o = outgoing; o; o = o->q_next) {
04444             if (!o->chan) {
04445                continue;
04446             }
04447             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
04448                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
04449                break;
04450             }
04451          }
04452       }
04453    } else { /* peer is valid */
04454       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
04455          we will always return with -1 so that it is hung up properly after the
04456          conversation.  */
04457       if (!strcmp(qe->chan->tech->type, "DAHDI"))
04458          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04459       if (!strcmp(peer->tech->type, "DAHDI"))
04460          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
04461       /* Update parameters for the queue */
04462       time(&now);
04463       recalc_holdtime(qe, (now - qe->start));
04464       ao2_lock(qe->parent);
04465       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
04466       ao2_unlock(qe->parent);
04467       member = lpeer->member;
04468       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
04469       ao2_ref(member, 1);
04470       hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere);
04471       outgoing = NULL;
04472       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
04473          int res2;
04474 
04475          res2 = ast_autoservice_start(qe->chan);
04476          if (!res2) {
04477             if (qe->parent->memberdelay) {
04478                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
04479                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
04480             }
04481             if (!res2 && announce) {
04482                play_file(peer, announce);
04483             }
04484             if (!res2 && qe->parent->reportholdtime) {
04485                if (!play_file(peer, qe->parent->sound_reporthold)) {
04486                   int holdtime, holdtimesecs;
04487 
04488                   time(&now);
04489                   holdtime = abs((now - qe->start) / 60);
04490                   holdtimesecs = abs((now - qe->start) % 60);
04491                   if (holdtime > 0) {
04492                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
04493                      play_file(peer, qe->parent->sound_minutes);
04494                   }
04495                   if (holdtimesecs > 1) {
04496                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
04497                      play_file(peer, qe->parent->sound_seconds);
04498                   }
04499                }
04500             }
04501          }
04502          res2 |= ast_autoservice_stop(qe->chan);
04503          if (ast_check_hangup(peer)) {
04504             /* Agent must have hung up */
04505             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
04506             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
04507             if (qe->parent->eventwhencalled)
04508                manager_event(EVENT_FLAG_AGENT, "AgentDump",
04509                      "Queue: %s\r\n"
04510                      "Uniqueid: %s\r\n"
04511                      "Channel: %s\r\n"
04512                      "Member: %s\r\n"
04513                      "MemberName: %s\r\n"
04514                      "%s",
04515                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04516                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04517             ast_hangup(peer);
04518             ao2_ref(member, -1);
04519             goto out;
04520          } else if (res2) {
04521             /* Caller must have hung up just before being connected*/
04522             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
04523             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
04524             record_abandoned(qe);
04525             ast_hangup(peer);
04526             ao2_ref(member, -1);
04527             return -1;
04528          }
04529       }
04530       /* Stop music on hold */
04531       if (ringing)
04532          ast_indicate(qe->chan,-1);
04533       else
04534          ast_moh_stop(qe->chan);
04535       /* If appropriate, log that we have a destination channel */
04536       if (qe->chan->cdr)
04537          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
04538       /* Make sure channels are compatible */
04539       res = ast_channel_make_compatible(qe->chan, peer);
04540       if (res < 0) {
04541          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
04542          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
04543          record_abandoned(qe);
04544          ast_cdr_failed(qe->chan->cdr);
04545          ast_hangup(peer);
04546          ao2_ref(member, -1);
04547          return -1;
04548       }
04549 
04550       /* Play announcement to the caller telling it's his turn if defined */
04551       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
04552          if (play_file(qe->chan, qe->parent->sound_callerannounce))
04553             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
04554       }
04555 
04556       ao2_lock(qe->parent);
04557       /* if setinterfacevar is defined, make member variables available to the channel */
04558       /* use  pbx_builtin_setvar to set a load of variables with one call */
04559       if (qe->parent->setinterfacevar) {
04560          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
04561             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
04562          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04563          pbx_builtin_setvar_multiple(peer, interfacevar);
04564       }
04565       
04566       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
04567       /* use  pbx_builtin_setvar to set a load of variables with one call */
04568       if (qe->parent->setqueueentryvar) {
04569          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
04570             (long) time(NULL) - qe->start, qe->opos);
04571          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
04572          pbx_builtin_setvar_multiple(peer, interfacevar);
04573       }
04574    
04575       ao2_unlock(qe->parent);
04576 
04577       /* try to set queue variables if configured to do so*/
04578       set_queue_variables(qe->parent, qe->chan);
04579       set_queue_variables(qe->parent, peer);
04580       
04581       ast_channel_lock(qe->chan);
04582       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
04583             monitorfilename = ast_strdupa(monitorfilename);
04584       }
04585       ast_channel_unlock(qe->chan);
04586       /* Begin Monitoring */
04587       if (qe->parent->monfmt && *qe->parent->monfmt) {
04588          if (!qe->parent->montype) {
04589             const char *monexec, *monargs;
04590             ast_debug(1, "Starting Monitor as requested.\n");
04591             ast_channel_lock(qe->chan);
04592             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
04593                which = qe->chan;
04594                monexec = monexec ? ast_strdupa(monexec) : NULL;
04595             }
04596             else
04597                which = peer;
04598             ast_channel_unlock(qe->chan);
04599             if (monitorfilename) {
04600                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
04601             } else if (qe->chan->cdr) {
04602                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
04603             } else {
04604                /* Last ditch effort -- no CDR, make up something */
04605                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04606                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
04607             }
04608             if (!ast_strlen_zero(monexec)) {
04609                ast_monitor_setjoinfiles(which, 1);
04610             }
04611          } else {
04612             mixmonapp = pbx_findapp("MixMonitor");
04613             
04614             if (mixmonapp) {
04615                ast_debug(1, "Starting MixMonitor as requested.\n");
04616                if (!monitorfilename) {
04617                   if (qe->chan->cdr)
04618                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
04619                   else
04620                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
04621                } else {
04622                   const char *m = monitorfilename;
04623                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
04624                      switch (*m) {
04625                      case '^':
04626                         if (*(m + 1) == '{')
04627                            *p = '$';
04628                         break;
04629                      case ',':
04630                         *p++ = '\\';
04631                         /* Fall through */
04632                      default:
04633                         *p = *m;
04634                      }
04635                      if (*m == '\0')
04636                         break;
04637                   }
04638                   if (p == tmpid2 + sizeof(tmpid2))
04639                      tmpid2[sizeof(tmpid2) - 1] = '\0';
04640 
04641                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
04642                }
04643 
04644                ast_channel_lock(qe->chan);
04645                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
04646                      monitor_exec = ast_strdupa(monitor_exec);
04647                }
04648                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
04649                      monitor_options = ast_strdupa(monitor_options);
04650                } else {
04651                   monitor_options = "";
04652                }
04653                ast_channel_unlock(qe->chan);
04654 
04655                if (monitor_exec) {
04656                   const char *m = monitor_exec;
04657                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
04658                      switch (*m) {
04659                      case '^':
04660                         if (*(m + 1) == '{')
04661                            *p = '$';
04662                         break;
04663                      case ',':
04664                         *p++ = '\\';
04665                         /* Fall through */
04666                      default:
04667                         *p = *m;
04668                      }
04669                      if (*m == '\0')
04670                         break;
04671                   }
04672                   if (p == meid2 + sizeof(meid2))
04673                      meid2[sizeof(meid2) - 1] = '\0';
04674 
04675                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
04676                }
04677    
04678                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
04679 
04680                if (!ast_strlen_zero(monitor_exec))
04681                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
04682                else
04683                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
04684                
04685                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
04686                /* We purposely lock the CDR so that pbx_exec does not update the application data */
04687                if (qe->chan->cdr)
04688                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04689                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
04690                if (qe->chan->cdr)
04691                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
04692 
04693             } else {
04694                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
04695             }
04696          }
04697       }
04698       /* Drop out of the queue at this point, to prepare for next caller */
04699       leave_queue(qe);        
04700       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
04701          ast_debug(1, "app_queue: sendurl=%s.\n", url);
04702          ast_channel_sendurl(peer, url);
04703       }
04704       
04705       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
04706       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
04707       if (!ast_strlen_zero(macro)) {
04708             macroexec = ast_strdupa(macro);
04709       } else {
04710          if (qe->parent->membermacro)
04711             macroexec = ast_strdupa(qe->parent->membermacro);
04712       }
04713 
04714       if (!ast_strlen_zero(macroexec)) {
04715          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
04716          
04717          res = ast_autoservice_start(qe->chan);
04718          if (res) {
04719             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04720             res = -1;
04721          }
04722          
04723          application = pbx_findapp("Macro");
04724 
04725          if (application) {
04726             res = pbx_exec(peer, application, macroexec);
04727             ast_debug(1, "Macro exited with status %d\n", res);
04728             res = 0;
04729          } else {
04730             ast_log(LOG_ERROR, "Could not find application Macro\n");
04731             res = -1;
04732          }
04733 
04734          if (ast_autoservice_stop(qe->chan) < 0) {
04735             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04736             res = -1;
04737          }
04738       }
04739 
04740       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
04741       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
04742       if (!ast_strlen_zero(gosub)) {
04743             gosubexec = ast_strdupa(gosub);
04744       } else {
04745          if (qe->parent->membergosub)
04746             gosubexec = ast_strdupa(qe->parent->membergosub);
04747       }
04748 
04749       if (!ast_strlen_zero(gosubexec)) {
04750          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
04751          
04752          res = ast_autoservice_start(qe->chan);
04753          if (res) {
04754             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
04755             res = -1;
04756          }
04757          
04758          application = pbx_findapp("Gosub");
04759          
04760          if (application) {
04761             char *gosub_args, *gosub_argstart;
04762 
04763             /* Set where we came from */
04764             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
04765             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
04766             peer->priority = 0;
04767 
04768             gosub_argstart = strchr(gosubexec, ',');
04769             if (gosub_argstart) {
04770                *gosub_argstart = 0;
04771                if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
04772                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04773                   gosub_args = NULL;
04774                }
04775                *gosub_argstart = ',';
04776             } else {
04777                if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
04778                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04779                   gosub_args = NULL;
04780                }
04781             }
04782             if (gosub_args) {
04783                res = pbx_exec(peer, application, gosub_args);
04784                if (!res) {
04785                   struct ast_pbx_args args;
04786                   memset(&args, 0, sizeof(args));
04787                   args.no_hangup_chan = 1;
04788                   ast_pbx_run_args(peer, &args);
04789                }
04790                ast_free(gosub_args);
04791                ast_debug(1, "Gosub exited with status %d\n", res);
04792             } else {
04793                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
04794             }
04795          } else {
04796             ast_log(LOG_ERROR, "Could not find application Gosub\n");
04797             res = -1;
04798          }
04799       
04800          if (ast_autoservice_stop(qe->chan) < 0) {
04801             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04802             res = -1;
04803          }
04804       }
04805 
04806       if (!ast_strlen_zero(agi)) {
04807          ast_debug(1, "app_queue: agi=%s.\n", agi);
04808          application = pbx_findapp("agi");
04809          if (application) {
04810             agiexec = ast_strdupa(agi);
04811             ret = pbx_exec(qe->chan, application, agiexec);
04812          } else
04813             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
04814       }
04815       qe->handled++;
04816       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
04817                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
04818       if (update_cdr && qe->chan->cdr) 
04819          ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
04820       if (qe->parent->eventwhencalled)
04821          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
04822                "Queue: %s\r\n"
04823                "Uniqueid: %s\r\n"
04824                "Channel: %s\r\n"
04825                "Member: %s\r\n"
04826                "MemberName: %s\r\n"
04827                "Holdtime: %ld\r\n"
04828                "BridgedChannel: %s\r\n"
04829                "Ringtime: %ld\r\n"
04830                "%s",
04831                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04832                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
04833                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04834       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
04835       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
04836    
04837       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
04838          queue_end_bridge->q = qe->parent;
04839          queue_end_bridge->chan = qe->chan;
04840          bridge_config.end_bridge_callback = end_bridge_callback;
04841          bridge_config.end_bridge_callback_data = queue_end_bridge;
04842          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
04843          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
04844           * to make sure to increase the refcount of this queue so it cannot be freed until we
04845           * are done with it. We remove this reference in end_bridge_callback.
04846           */
04847          queue_t_ref(qe->parent, "For bridge_config reference");
04848       }
04849 
04850       time(&callstart);
04851       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
04852       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
04853 
04854       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
04855        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
04856        * the AgentComplete manager event
04857        */
04858       ast_channel_lock(qe->chan);
04859       if (!attended_transfer_occurred(qe->chan)) {
04860          struct ast_datastore *tds;
04861 
04862          /* detect a blind transfer */
04863          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
04864             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04865                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
04866                (long) (time(NULL) - callstart), qe->opos);
04867             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
04868          } else if (ast_check_hangup(qe->chan)) {
04869             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
04870                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04871             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
04872          } else {
04873             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04874                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04875             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04876          }
04877          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
04878             ast_channel_datastore_remove(qe->chan, tds);
04879          }
04880          update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04881       } else {
04882          /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
04883          send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
04884       }
04885 
04886       if (transfer_ds) {
04887          ast_datastore_free(transfer_ds);
04888       }
04889       ast_channel_unlock(qe->chan);
04890       ast_hangup(peer);
04891       res = bridge ? bridge : 1;
04892       ao2_ref(member, -1);
04893    }
04894 out:
04895    hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere);
04896 
04897    return res;
04898 }

static int unload_module ( void   )  [static]

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

08071 {
08072    int res;
08073    struct ast_context *con;
08074    struct ao2_iterator q_iter;
08075    struct call_queue *q = NULL;
08076 
08077    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08078    res = ast_manager_unregister("QueueStatus");
08079    res |= ast_manager_unregister("Queues");
08080    res |= ast_manager_unregister("QueueRule");
08081    res |= ast_manager_unregister("QueueSummary");
08082    res |= ast_manager_unregister("QueueAdd");
08083    res |= ast_manager_unregister("QueueRemove");
08084    res |= ast_manager_unregister("QueuePause");
08085    res |= ast_manager_unregister("QueueLog");
08086    res |= ast_manager_unregister("QueuePenalty");
08087    res |= ast_unregister_application(app_aqm);
08088    res |= ast_unregister_application(app_rqm);
08089    res |= ast_unregister_application(app_pqm);
08090    res |= ast_unregister_application(app_upqm);
08091    res |= ast_unregister_application(app_ql);
08092    res |= ast_unregister_application(app);
08093    res |= ast_custom_function_unregister(&queueexists_function);
08094    res |= ast_custom_function_unregister(&queuevar_function);
08095    res |= ast_custom_function_unregister(&queuemembercount_function);
08096    res |= ast_custom_function_unregister(&queuemembercount_dep);
08097    res |= ast_custom_function_unregister(&queuememberlist_function);
08098    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08099    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08100 
08101    res |= ast_data_unregister(NULL);
08102 
08103    if (device_state_sub)
08104       ast_event_unsubscribe(device_state_sub);
08105 
08106    ast_extension_state_del(0, extension_state_cb);
08107 
08108    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08109       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08110       ast_context_destroy(con, "app_queue"); /* leave no trace */
08111    }
08112 
08113    q_iter = ao2_iterator_init(queues, 0);
08114    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08115       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08116       queue_t_unref(q, "Done with iterator");
08117    }
08118    ao2_iterator_destroy(&q_iter);
08119    ao2_ref(queues, -1);
08120    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08121    ast_unload_realtime("queue_members");
08122    return res;
08123 }

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

References 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, ast_channel::name, pbx_builtin_setvar_helper(), queue_ent::pr, and penalty_rule::time.

Referenced by queue_exec().

03725 {
03726    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
03727    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
03728    char max_penalty_str[20], min_penalty_str[20]; 
03729    /* a relative change to the penalty could put it below 0 */
03730    if (max_penalty < 0)
03731       max_penalty = 0;
03732    if (min_penalty < 0)
03733       min_penalty = 0;
03734    if (min_penalty > max_penalty)
03735       min_penalty = max_penalty;
03736    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
03737    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
03738    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
03739    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
03740    qe->max_penalty = max_penalty;
03741    qe->min_penalty = min_penalty;
03742    ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time);
03743    qe->pr = AST_LIST_NEXT(qe->pr, list);
03744 }

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

03833 {
03834    int oldtalktime;
03835 
03836    struct member *mem;
03837    struct call_queue *qtmp;
03838    struct ao2_iterator queue_iter;  
03839    
03840    if (shared_lastcall) {
03841       queue_iter = ao2_iterator_init(queues, 0);
03842       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03843          ao2_lock(qtmp);
03844          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03845             time(&mem->lastcall);
03846             mem->calls++;
03847             mem->lastqueue = q;
03848             ao2_ref(mem, -1);
03849          }
03850          ao2_unlock(qtmp);
03851          queue_t_unref(qtmp, "Done with iterator");
03852       }
03853       ao2_iterator_destroy(&queue_iter);
03854    } else {
03855       ao2_lock(q);
03856       time(&member->lastcall);
03857       member->calls++;
03858       member->lastqueue = q;
03859       ao2_unlock(q);
03860    }  
03861    ao2_lock(q);
03862    q->callscompleted++;
03863    if (callcompletedinsl)
03864       q->callscompletedinsl++;
03865    /* Calculate talktime using the same exponential average as holdtime code*/
03866    oldtalktime = q->talktime;
03867    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
03868    ao2_unlock(q);
03869    return 0;
03870 }

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

Definition at line 2228 of file app_queue.c.

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

Referenced by set_member_paused().

02229 {
02230    int ret = -1;
02231 
02232    if (ast_strlen_zero(mem->rt_uniqueid))
02233       return ret;
02234 
02235    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02236       ret = 0;
02237 
02238    return ret;
02239 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 2242 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_variable_retrieve(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, queues, member::realtime, rt_handle_member_record(), S_OR, and SENTINEL.

Referenced by load_realtime_queue(), and queue_exec().

02243 {
02244    struct ast_config *member_config = NULL;
02245    struct member *m;
02246    char *interface = NULL;
02247    struct ao2_iterator mem_iter;
02248 
02249    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02250       /*This queue doesn't have realtime members*/
02251       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02252       return;
02253    }
02254 
02255    ao2_lock(queues);
02256    ao2_lock(q);
02257    
02258    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
02259    mem_iter = ao2_iterator_init(q->members, 0);
02260    while ((m = ao2_iterator_next(&mem_iter))) {
02261       if (m->realtime)
02262          m->dead = 1;
02263       ao2_ref(m, -1);
02264    }
02265    ao2_iterator_destroy(&mem_iter);
02266 
02267    while ((interface = ast_category_browse(member_config, interface))) {
02268       rt_handle_member_record(q, interface,
02269          ast_variable_retrieve(member_config, interface, "uniqueid"),
02270          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02271          ast_variable_retrieve(member_config, interface, "penalty"),
02272          ast_variable_retrieve(member_config, interface, "paused"),
02273          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02274    }
02275 
02276    /* Delete all realtime members that have been deleted in DB. */
02277    mem_iter = ao2_iterator_init(q->members, 0);
02278    while ((m = ao2_iterator_next(&mem_iter))) {
02279       if (m->dead) {
02280          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02281          ao2_unlink(q->members, m);
02282          q->membercount--;
02283       }
02284       ao2_ref(m, -1);
02285    }
02286    ao2_iterator_destroy(&mem_iter);
02287    ao2_unlock(q);
02288    ao2_unlock(queues);
02289    ast_config_destroy(member_config);
02290 }

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

References EVENT_FLAG_AGENT, and manager_event.

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

01268 {
01269    m->status = status;
01270 
01271    if (q->maskmemberstatus)
01272       return 0;
01273 
01274    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01275       "Queue: %s\r\n"
01276       "Location: %s\r\n"
01277       "MemberName: %s\r\n"
01278       "Membership: %s\r\n"
01279       "Penalty: %d\r\n"
01280       "CallsTaken: %d\r\n"
01281       "LastCall: %d\r\n"
01282       "Status: %d\r\n"
01283       "Paused: %d\r\n",
01284       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01285       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01286    );
01287 
01288    return 0;
01289 }

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

UnPauseQueueMember application.

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

05384 {
05385    char *parse;
05386    AST_DECLARE_APP_ARGS(args,
05387       AST_APP_ARG(queuename);
05388       AST_APP_ARG(interface);
05389       AST_APP_ARG(options);
05390       AST_APP_ARG(reason);
05391    );
05392 
05393    if (ast_strlen_zero(data)) {
05394       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05395       return -1;
05396    }
05397 
05398    parse = ast_strdupa(data);
05399 
05400    AST_STANDARD_APP_ARGS(args, parse);
05401 
05402    if (ast_strlen_zero(args.interface)) {
05403       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05404       return -1;
05405    }
05406 
05407    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05408       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05409       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05410       return 0;
05411    }
05412 
05413    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05414 
05415    return 0;
05416 }

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 2398 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(), and wait_a_bit().

02399 {
02400    int digitlen = strlen(qe->digits);
02401 
02402    /* Prevent possible buffer overflow */
02403    if (digitlen < sizeof(qe->digits) - 2) {
02404       qe->digits[digitlen] = digit;
02405       qe->digits[digitlen + 1] = '\0';
02406    } else {
02407       qe->digits[0] = '\0';
02408       return 0;
02409    }
02410 
02411    /* If there's no context to goto, short-circuit */
02412    if (ast_strlen_zero(qe->context))
02413       return 0;
02414 
02415    /* If the extension is bad, then reset the digits to blank */
02416    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02417       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02418       qe->digits[0] = '\0';
02419       return 0;
02420    }
02421 
02422    /* We have an exact match */
02423    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02424       qe->valid_digits = 1;
02425       /* Return 1 on a successful goto */
02426       return 1;
02427    }
02428 
02429    return 0;
02430 }

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

References ast_copy_string(), ast_str_buffer(), ast_str_thread_get(), queue_ent::chan, and pbx_builtin_serialize_variables().

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

02789 {
02790    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
02791    const char *tmp;
02792 
02793    if (pbx_builtin_serialize_variables(chan, &buf)) {
02794       int i, j;
02795 
02796       /* convert "\n" to "\nVariable: " */
02797       strcpy(vars, "Variable: ");
02798       tmp = ast_str_buffer(buf);
02799 
02800       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02801          vars[j] = tmp[i];
02802 
02803          if (tmp[i + 1] == '\0')
02804             break;
02805          if (tmp[i] == '\n') {
02806             vars[j++] = '\r';
02807             vars[j++] = '\n';
02808 
02809             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02810             j += 9;
02811          }
02812       }
02813       if (j > len - 3)
02814          j = len - 3;
02815       vars[j++] = '\r';
02816       vars[j++] = '\n';
02817       vars[j] = '\0';
02818    } else {
02819       /* there are no channel variables; leave it blank */
02820       *vars = '\0';
02821    }
02822    return vars;
02823 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 4900 of file app_queue.c.

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

Referenced by queue_exec().

04901 {
04902    /* Don't need to hold the lock while we setup the outgoing calls */
04903    int retrywait = qe->parent->retry * 1000;
04904 
04905    int res = ast_waitfordigit(qe->chan, retrywait);
04906    if (res > 0 && !valid_exit(qe, res))
04907       res = 0;
04908 
04909    return res;
04910 }

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]

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

References ast_channel_lock, ast_channel_unlock, AST_MAX_WATCHERS, ast_party_connected_line_init(), ast_poll_channel_add(), ast_strdupa, callattempt::call_next, callattempt::chan, queue_ent::chan, f, ast_channel::name, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, status, callattempt::stillgoing, and call_queue::strategy.

03265 {
03266    const char *queue = qe->parent->name;
03267    struct callattempt *o, *start = NULL, *prev = NULL;
03268    int res;
03269    int status;
03270    int numbusies = prebusies;
03271    int numnochan = 0;
03272    int stillgoing = 0;
03273    int orig = *to;
03274    struct ast_frame *f;
03275    struct callattempt *peer = NULL;
03276    struct ast_channel *winner;
03277    struct ast_channel *in = qe->chan;
03278    char on[80] = "";
03279    char membername[80] = "";
03280    long starttime = 0;
03281    long endtime = 0;
03282 #ifdef HAVE_EPOLL
03283    struct callattempt *epollo;
03284 #endif
03285    struct ast_party_connected_line connected_caller;
03286    char *inchan_name;
03287 
03288    ast_party_connected_line_init(&connected_caller);
03289 
03290    ast_channel_lock(qe->chan);
03291    inchan_name = ast_strdupa(qe->chan->name);
03292    ast_channel_unlock(qe->chan);
03293 
03294    starttime = (long) time(NULL);
03295 #ifdef HAVE_EPOLL
03296    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03297       if (epollo->chan)
03298          ast_poll_channel_add(in, epollo->chan);
03299    }
03300 #endif
03301    
03302    while (*to && !peer) {
03303       int numlines, retry, pos = 1;
03304       struct ast_channel *watchers[AST_MAX_WATCHERS];
03305       watchers[0] = in;
03306       start = NULL;
03307 
03308       for (retry = 0; retry < 2; retry++) {
03309          numlines = 0;
03310          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
03311             if (o->stillgoing) { /* Keep track of important channels */
03312                stillgoing = 1;
03313                if (o->chan) {
03314                   watchers[pos++] = o->chan;
03315                   if (!start)
03316                      start = o;
03317                   else
03318                      prev->call_next = o;
03319                   prev = o;
03320                }
03321             }
03322             numlines++;
03323          }
03324          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
03325             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
03326             break;
03327          /* On "ringall" strategy we only move to the next penalty level
03328             when *all* ringing phones are done in the current penalty level */
03329          ring_one(qe, outgoing, &numbusies);
03330          /* and retry... */
03331       }
03332       if (pos == 1 /* not found */) {
03333          if (numlines == (numbusies + numnochan)) {
03334             ast_debug(1, "Everyone is busy at this time\n");
03335          } else {
03336             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03337          }
03338          *to = 0;
03339          return NULL;
03340       }
03341 
03342       /* Poll for events from both the incoming channel as well as any outgoing channels */
03343       winner = ast_waitfor_n(watchers, pos, to);
03344 
03345       /* Service all of the outgoing channels */
03346       for (o = start; o; o = o->call_next) {
03347          /* We go with a static buffer here instead of using ast_strdupa. Using
03348           * ast_strdupa in a loop like this one can cause a stack overflow
03349           */
03350          char ochan_name[AST_CHANNEL_NAME];
03351          if (o->chan) {
03352             ast_channel_lock(o->chan);
03353             ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name));
03354             ast_channel_unlock(o->chan);
03355          }
03356          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
03357             if (!peer) {
03358                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03359                if (update_connectedline) {
03360                   if (o->pending_connected_update) {
03361                      if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03362                         ast_channel_update_connected_line(in, &o->connected, NULL);
03363                      }
03364                   } else if (!o->dial_callerid_absent) {
03365                      ast_channel_lock(o->chan);
03366                      ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03367                      ast_channel_unlock(o->chan);
03368                      connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03369                      ast_channel_update_connected_line(in, &connected_caller, NULL);
03370                      ast_party_connected_line_free(&connected_caller);
03371                   }
03372                }
03373                if (o->aoc_s_rate_list) {
03374                   size_t encoded_size;
03375                   struct ast_aoc_encoded *encoded;
03376                   if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03377                      ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03378                      ast_aoc_destroy_encoded(encoded);
03379                   }
03380                }
03381                peer = o;
03382             }
03383          } else if (o->chan && (o->chan == winner)) {
03384 
03385             ast_copy_string(on, o->member->interface, sizeof(on));
03386             ast_copy_string(membername, o->member->membername, sizeof(membername));
03387 
03388             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03389                ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03390                numnochan++;
03391                do_hang(o);
03392                winner = NULL;
03393                continue;
03394             } else if (!ast_strlen_zero(o->chan->call_forward)) {
03395                struct ast_channel *original = o->chan;
03396                char tmpchan[256];
03397                char *stuff;
03398                char *tech;
03399 
03400                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03401                if ((stuff = strchr(tmpchan, '/'))) {
03402                   *stuff++ = '\0';
03403                   tech = tmpchan;
03404                } else {
03405                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03406                   stuff = tmpchan;
03407                   tech = "Local";
03408                }
03409 
03410                ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03411 
03412                /* Before processing channel, go ahead and check for forwarding */
03413                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03414                /* Setup parameters */
03415                o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03416                if (!o->chan) {
03417                   ast_log(LOG_NOTICE,
03418                      "Forwarding failed to create channel to dial '%s/%s'\n",
03419                      tech, stuff);
03420                   o->stillgoing = 0;
03421                   numnochan++;
03422                } else {
03423                   struct ast_party_redirecting redirecting;
03424 
03425                   ast_channel_lock(o->chan);
03426                   while (ast_channel_trylock(in)) {
03427                      CHANNEL_DEADLOCK_AVOIDANCE(o->chan);
03428                   }
03429                   ast_channel_inherit_variables(in, o->chan);
03430                   ast_channel_datastore_inherit(in, o->chan);
03431 
03432                   ast_string_field_set(o->chan, accountcode, in->accountcode);
03433 
03434                   ast_channel_set_redirecting(o->chan, &original->redirecting, NULL);
03435                   if (!o->chan->redirecting.from.number.valid
03436                      || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03437                      /*
03438                       * The call was not previously redirected so it is
03439                       * now redirected from this number.
03440                       */
03441                      ast_party_number_free(&o->chan->redirecting.from.number);
03442                      ast_party_number_init(&o->chan->redirecting.from.number);
03443                      o->chan->redirecting.from.number.valid = 1;
03444                      o->chan->redirecting.from.number.str =
03445                         ast_strdup(S_OR(in->macroexten, in->exten));
03446                   }
03447 
03448                   o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03449 
03450                   ast_party_caller_copy(&o->chan->caller, &in->caller);
03451                   ast_party_connected_line_copy(&o->chan->connected, &original->connected);
03452 
03453                   /*
03454                    * We must unlock o->chan before calling
03455                    * ast_channel_redirecting_macro, because we put o->chan into
03456                    * autoservice there.  That is pretty much a guaranteed
03457                    * deadlock.  This is why the handling of o->chan's lock may
03458                    * seem a bit unusual here.
03459                    */
03460                   ast_party_redirecting_init(&redirecting);
03461                   ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03462                   ast_channel_unlock(o->chan);
03463                   res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0);
03464                   if (res) {
03465                      ast_channel_update_redirecting(in, &redirecting, NULL);
03466                   }
03467                   ast_party_redirecting_free(&redirecting);
03468                   ast_channel_unlock(in);
03469 
03470                   update_connectedline = 1;
03471 
03472                   if (ast_call(o->chan, stuff, 0)) {
03473                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03474                         tech, stuff);
03475                      do_hang(o);
03476                      numnochan++;
03477                   }
03478                }
03479                /* Hangup the original channel now, in case we needed it */
03480                ast_hangup(winner);
03481                continue;
03482             }
03483             f = ast_read(winner);
03484             if (f) {
03485                if (f->frametype == AST_FRAME_CONTROL) {
03486                   switch (f->subclass.integer) {
03487                   case AST_CONTROL_ANSWER:
03488                      /* This is our guy if someone answered. */
03489                      if (!peer) {
03490                         ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03491                         if (update_connectedline) {
03492                            if (o->pending_connected_update) {
03493                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03494                                  ast_channel_update_connected_line(in, &o->connected, NULL);
03495                               }
03496                            } else if (!o->dial_callerid_absent) {
03497                               ast_channel_lock(o->chan);
03498                               ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03499                               ast_channel_unlock(o->chan);
03500                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03501                               ast_channel_update_connected_line(in, &connected_caller, NULL);
03502                               ast_party_connected_line_free(&connected_caller);
03503                            }
03504                         }
03505                         if (o->aoc_s_rate_list) {
03506                            size_t encoded_size;
03507                            struct ast_aoc_encoded *encoded;
03508                            if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03509                               ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03510                               ast_aoc_destroy_encoded(encoded);
03511                            }
03512                         }
03513                         peer = o;
03514                      }
03515                      break;
03516                   case AST_CONTROL_BUSY:
03517                      ast_verb(3, "%s is busy\n", ochan_name);
03518                      if (in->cdr)
03519                         ast_cdr_busy(in->cdr);
03520                      do_hang(o);
03521                      endtime = (long) time(NULL);
03522                      endtime -= starttime;
03523                      rna(endtime * 1000, qe, on, membername, 0);
03524                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03525                         if (qe->parent->timeoutrestart)
03526                            *to = orig;
03527                         /* Have enough time for a queue member to answer? */
03528                         if (*to > 500) {
03529                            ring_one(qe, outgoing, &numbusies);
03530                            starttime = (long) time(NULL);
03531                         }
03532                      }
03533                      numbusies++;
03534                      break;
03535                   case AST_CONTROL_CONGESTION:
03536                      ast_verb(3, "%s is circuit-busy\n", ochan_name);
03537                      if (in->cdr)
03538                         ast_cdr_busy(in->cdr);
03539                      endtime = (long) time(NULL);
03540                      endtime -= starttime;
03541                      rna(endtime * 1000, qe, on, membername, 0);
03542                      do_hang(o);
03543                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03544                         if (qe->parent->timeoutrestart)
03545                            *to = orig;
03546                         if (*to > 500) {
03547                            ring_one(qe, outgoing, &numbusies);
03548                            starttime = (long) time(NULL);
03549                         }
03550                      }
03551                      numbusies++;
03552                      break;
03553                   case AST_CONTROL_RINGING:
03554                      ast_verb(3, "%s is ringing\n", ochan_name);
03555 
03556                      /* Start ring indication when the channel is ringing, if specified */
03557                      if (qe->ring_when_ringing) {
03558                         ast_moh_stop(qe->chan);
03559                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
03560                      }
03561                      break;
03562                   case AST_CONTROL_OFFHOOK:
03563                      /* Ignore going off hook */
03564                      break;
03565                   case AST_CONTROL_CONNECTED_LINE:
03566                      if (!update_connectedline) {
03567                         ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03568                      } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03569                         struct ast_party_connected_line connected;
03570                         ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03571                         ast_party_connected_line_set_init(&connected, &o->connected);
03572                         ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03573                         ast_party_connected_line_set(&o->connected, &connected, NULL);
03574                         ast_party_connected_line_free(&connected);
03575                         o->pending_connected_update = 1;
03576                      } else {
03577                         if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03578                            ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03579                         }
03580                      }
03581                      break;
03582                   case AST_CONTROL_AOC:
03583                      {
03584                         struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03585                         if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03586                            ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03587                            o->aoc_s_rate_list = decoded;
03588                         } else {
03589                            ast_aoc_destroy_decoded(decoded);
03590                         }
03591                      }
03592                      break;
03593                   case AST_CONTROL_REDIRECTING:
03594                      if (!update_connectedline) {
03595                         ast_verb(3, "Redirecting update to %s prevented\n", inchan_name);
03596                      } else {
03597                         ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name);
03598                         if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
03599                            ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
03600                         }
03601                      }
03602                      break;
03603                   default:
03604                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
03605                      break;
03606                   }
03607                }
03608                ast_frfree(f);
03609             } else { /* ast_read() returned NULL */
03610                endtime = (long) time(NULL) - starttime;
03611                rna(endtime * 1000, qe, on, membername, 1);
03612                do_hang(o);
03613                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03614                   if (qe->parent->timeoutrestart)
03615                      *to = orig;
03616                   if (*to > 500) {
03617                      ring_one(qe, outgoing, &numbusies);
03618                      starttime = (long) time(NULL);
03619                   }
03620                }
03621             }
03622          }
03623       }
03624 
03625       /* If we received an event from the caller, deal with it. */
03626       if (winner == in) {
03627          f = ast_read(in);
03628          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
03629             /* Got hung up */
03630             *to = -1;
03631             if (f) {
03632                if (f->data.uint32) {
03633                   in->hangupcause = f->data.uint32;
03634                }
03635                ast_frfree(f);
03636             }
03637             return NULL;
03638          }
03639          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
03640             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
03641             *to = 0;
03642             ast_frfree(f);
03643             return NULL;
03644          }
03645          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
03646             ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
03647             *to = 0;
03648             *digit = f->subclass.integer;
03649             ast_frfree(f);
03650             return NULL;
03651          }
03652          ast_frfree(f);
03653       }
03654       if (!*to) {
03655          for (o = start; o; o = o->call_next)
03656             rna(orig, qe, o->interface, o->member->membername, 1);
03657       }
03658    }
03659 
03660 #ifdef HAVE_EPOLL
03661    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03662       if (epollo->chan)
03663          ast_poll_channel_del(in, epollo->chan);
03664    }
03665 #endif
03666 
03667    return peer;
03668 }

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

References ast_queue_log(), queue_ent::chan, 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, queue_ent::pos, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, queue_ent::start, status, and ast_channel::uniqueid.

Referenced by queue_exec().

03757 {
03758    int res = 0;
03759 
03760    /* This is the holding pen for callers 2 through maxlen */
03761    for (;;) {
03762 
03763       if (is_our_turn(qe))
03764          break;
03765 
03766       /* If we have timed out, break out */
03767       if (qe->expire && (time(NULL) >= qe->expire)) {
03768          *reason = QUEUE_TIMEOUT;
03769          break;
03770       }
03771 
03772       if (qe->parent->leavewhenempty) {
03773          int status = 0;
03774 
03775          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
03776             *reason = QUEUE_LEAVEEMPTY;
03777             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03778             leave_queue(qe);
03779             break;
03780          }
03781       }
03782 
03783       /* Make a position announcement, if enabled */
03784       if (qe->parent->announcefrequency &&
03785          (res = say_position(qe,ringing)))
03786          break;
03787 
03788       /* If we have timed out, break out */
03789       if (qe->expire && (time(NULL) >= qe->expire)) {
03790          *reason = QUEUE_TIMEOUT;
03791          break;
03792       }
03793 
03794       /* Make a periodic announcement, if enabled */
03795       if (qe->parent->periodicannouncefrequency &&
03796          (res = say_periodic_announcement(qe,ringing)))
03797          break;
03798       
03799       /* see if we need to move to the next penalty level for this queue */
03800       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03801          update_qe_rule(qe);
03802       }
03803 
03804       /* If we have timed out, break out */
03805       if (qe->expire && (time(NULL) >= qe->expire)) {
03806          *reason = QUEUE_TIMEOUT;
03807          break;
03808       }
03809       
03810       /* Wait a second before checking again */
03811       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03812          if (res > 0 && !valid_exit(qe, res))
03813             res = 0;
03814          else
03815             break;
03816       }
03817       
03818       /* If we have timed out, break out */
03819       if (qe->expire && (time(NULL) >= qe->expire)) {
03820          *reason = QUEUE_TIMEOUT;
03821          break;
03822       }
03823    }
03824 
03825    return res;
03826 }


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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", } [static]

Definition at line 8205 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 739 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 741 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 745 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 749 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 743 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 747 of file app_queue.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 8205 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 763 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

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

Referenced by aji_init_event_distribution(), load_module(), load_pbx(), and unload_module().

struct ast_taskprocessor* devicestate_tps [static]

Definition at line 723 of file app_queue.c.

Referenced by device_state_cb(), load_module(), and unload_module().

enum queue_result id

Definition at line 789 of file app_queue.c.

Referenced by _sip_show_peers(), _skinny_show_devices(), _skinny_show_lines(), amixer_max(), ast_cc_extension_monitor_add_dialstring(), ast_party_id_presentation(), frame_trace_helper(), idemodulator(), misdn_queue_connected_line_update(), misdn_update_caller_id(), party_id_build_data(), party_id_read(), party_id_write(), pidf_validate_tuple(), and setamixer().

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 766 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 752 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 7784 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 7790 of file app_queue.c.

struct ast_data_entry queue_data_providers[] [static]

Initial value:

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

Definition at line 8066 of file app_queue.c.

Referenced by load_module().

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 757 of file app_queue.c.

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

Referenced by set_queue_result().

struct ast_datastore_info queue_transfer_info [static]

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

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

struct ast_custom_function queueexists_function [static]

Initial value:

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

Definition at line 6242 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_dep [static]

Initial value:

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

Definition at line 6257 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Initial value:

 {
   .name = "QUEUE_MEMBER",
   .read = queue_function_qac,
}

Definition at line 6252 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Initial value:

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

Definition at line 6267 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberpenalty_function [static]

Initial value:

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

Definition at line 6272 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 1030 of file app_queue.c.

Referenced by compare_weight(), extension_state_cb(), find_queue_by_name_rt(), handle_statechange(), join_queue(), leave_queue(), load_realtime_queue(), update_queue(), and update_realtime_members().

struct ast_data_handler queues_data_provider [static]

Initial value:

Definition at line 8061 of file app_queue.c.

struct ast_custom_function queuevar_function [static]

Initial value:

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

Definition at line 6247 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Initial value:

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

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

int shared_lastcall = 1 [static]

queues.conf [general] option

Definition at line 769 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* text

Definition at line 790 of file app_queue.c.

Referenced by festival_exec(), iconv_read(), method_match(), process_sdp(), reqprep(), set_queue_result(), and sip_new().

int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 775 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 760 of file app_queue.c.


Generated on Wed Apr 6 11:29:50 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7