Mon Jun 27 16:50:59 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, QUEUE_STRATEGY_RRORDERED
}
enum  { QUEUE_AUTOPAUSE_OFF = 0, QUEUE_AUTOPAUSE_ON, QUEUE_AUTOPAUSE_ALL }
enum  agent_complete_reason { CALLER, AGENT, TRANSFER }
enum  empty_conditions {
  QUEUE_EMPTY_PENALTY = (1 << 0), QUEUE_EMPTY_PAUSED = (1 << 1), QUEUE_EMPTY_INUSE = (1 << 2), QUEUE_EMPTY_RINGING = (1 << 3),
  QUEUE_EMPTY_UNAVAILABLE = (1 << 4), QUEUE_EMPTY_INVALID = (1 << 5), QUEUE_EMPTY_UNKNOWN = (1 << 6), QUEUE_EMPTY_WRAPUP = (1 << 7)
}
enum  queue_reload_mask { QUEUE_RELOAD_PARAMETERS = (1 << 0), QUEUE_RELOAD_MEMBER = (1 << 1), QUEUE_RELOAD_RULES = (1 << 2), QUEUE_RESET_STATS = (1 << 3) }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7
}
enum  queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF }

Functions

static char * __queues_show (struct mansession *s, int fd, int argc, const char *const *argv)
 Show queue(s) status and statistics.
static void __reg_module (void)
static void __unreg_module (void)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
 Add member to queue.
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, const char *data)
 AddQueueMember application.
 AST_DATA_STRUCTURE (queue_ent, DATA_EXPORT_QUEUE_ENT)
 AST_DATA_STRUCTURE (member, DATA_EXPORT_MEMBER)
 AST_DATA_STRUCTURE (call_queue, DATA_EXPORT_CALL_QUEUE)
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
static int autopause2int (const char *autopause)
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
static void callattempt_free (struct callattempt *doomed)
static void clear_queue (struct call_queue *q)
static int clear_stats (const char *queuename)
 Facilitates resetting statistics for a queue.
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_pause_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_rule_show (const char *line, const char *word, int pos, int state)
static char * complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static void copy_rules (struct queue_ent *qe, const char *rulename)
 Copy rule from global list into specified queue.
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (void *obj)
 Free queue's member list then its string fields.
static void device_state_cb (const struct ast_event *event, void *unused)
static void do_hang (struct callattempt *o)
 common hangup actions
static void do_print (struct mansession *s, int fd, const char *str)
 direct ouput to manager or cli with proper terminator
static void dump_queue_members (struct call_queue *pm_queue)
 Dump all members in a specific queue to the database.
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static int extension_state_cb (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 1060 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 1061 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 1076 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 1075 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 1074 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 1073 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 3410 of file app_queue.c.

#define DATA_EXPORT_CALL_QUEUE ( MEMBER   ) 

Definition at line 7978 of file app_queue.c.

#define DATA_EXPORT_MEMBER ( MEMBER   ) 

Definition at line 8043 of file app_queue.c.

#define DATA_EXPORT_QUEUE_ENT ( MEMBER   ) 

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 885 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 891 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 914 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EVENT_VARIABLES   3

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

Referenced by leave_queue().

#define queue_t_unref ( a,
 )     queue_unref(a)

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

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

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

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 894 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 896 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 897 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 893 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 895 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 
QUEUE_STRATEGY_RRORDERED 

Definition at line 834 of file app_queue.c.

anonymous enum

Enumerator:
QUEUE_AUTOPAUSE_OFF 
QUEUE_AUTOPAUSE_ON 
QUEUE_AUTOPAUSE_ALL 

Definition at line 845 of file app_queue.c.

00845      {
00846      QUEUE_AUTOPAUSE_OFF = 0,
00847      QUEUE_AUTOPAUSE_ON,
00848      QUEUE_AUTOPAUSE_ALL
00849 };

enum agent_complete_reason

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 4109 of file app_queue.c.

04109                            {
04110    CALLER,
04111    AGENT,
04112    TRANSFER
04113 };

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

01048                       {
01049    QUEUE_EMPTY_PENALTY = (1 << 0),
01050    QUEUE_EMPTY_PAUSED = (1 << 1),
01051    QUEUE_EMPTY_INUSE = (1 << 2),
01052    QUEUE_EMPTY_RINGING = (1 << 3),
01053    QUEUE_EMPTY_UNAVAILABLE = (1 << 4),
01054    QUEUE_EMPTY_INVALID = (1 << 5),
01055    QUEUE_EMPTY_UNKNOWN = (1 << 6),
01056    QUEUE_EMPTY_WRAPUP = (1 << 7),
01057 };

enum queue_reload_mask

Enumerator:
QUEUE_RELOAD_PARAMETERS 
QUEUE_RELOAD_MEMBER 
QUEUE_RELOAD_RULES 
QUEUE_RESET_STATS 

Definition at line 851 of file app_queue.c.

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

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 937 of file app_queue.c.

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

enum queue_timeout_priority

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 962 of file app_queue.c.

00962                             {
00963    TIMEOUT_PRIORITY_APP,
00964    TIMEOUT_PRIORITY_CONF,
00965 };


Function Documentation

static char* __queues_show ( struct mansession s,
int  fd,
int  argc,
const char *const *  argv 
) [static]

Show queue(s) status and statistics.

List the queues strategy, calls processed, members logged in, other queue statistics such as avg hold time.

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

06891 {
06892    struct call_queue *q;
06893    struct ast_str *out = ast_str_alloca(240);
06894    int found = 0;
06895    time_t now = time(NULL);
06896    struct ao2_iterator queue_iter;
06897    struct ao2_iterator mem_iter;
06898 
06899    if (argc != 2 && argc != 3)
06900       return CLI_SHOWUSAGE;
06901 
06902    if (argc == 3) { /* specific queue */
06903       if ((q = load_realtime_queue(argv[2]))) {
06904          queue_t_unref(q, "Done with temporary pointer");
06905       }
06906    } else if (ast_check_realtime("queues")) {
06907       /* This block is to find any queues which are defined in realtime but
06908        * which have not yet been added to the in-core container
06909        */
06910       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
06911       char *queuename;
06912       if (cfg) {
06913          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
06914             if ((q = load_realtime_queue(queuename))) {
06915                queue_t_unref(q, "Done with temporary pointer");
06916             }
06917          }
06918          ast_config_destroy(cfg);
06919       }
06920    }
06921 
06922    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
06923    ao2_lock(queues);
06924    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06925       float sl;
06926       struct call_queue *realtime_queue = NULL;
06927 
06928       ao2_lock(q);
06929       /* This check is to make sure we don't print information for realtime
06930        * queues which have been deleted from realtime but which have not yet
06931        * been deleted from the in-core container
06932        */
06933       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
06934          ao2_unlock(q);
06935          queue_t_unref(q, "Done with iterator");
06936          continue;
06937       } else if (q->realtime) {
06938          queue_t_unref(realtime_queue, "Queue is already in memory");
06939       }
06940       if (argc == 3 && strcasecmp(q->name, argv[2])) {
06941          ao2_unlock(q);
06942          queue_t_unref(q, "Done with iterator");
06943          continue;
06944       }
06945       found = 1;
06946 
06947       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
06948       if (q->maxlen)
06949          ast_str_append(&out, 0, "%d", q->maxlen);
06950       else
06951          ast_str_append(&out, 0, "unlimited");
06952       sl = 0;
06953       if (q->callscompleted > 0)
06954          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06955       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
06956          int2strat(q->strategy), q->holdtime, q->talktime, q->weight,
06957          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
06958       do_print(s, fd, ast_str_buffer(out));
06959       if (!ao2_container_count(q->members))
06960          do_print(s, fd, "   No Members");
06961       else {
06962          struct member *mem;
06963 
06964          do_print(s, fd, "   Members: ");
06965          mem_iter = ao2_iterator_init(q->members, 0);
06966          while ((mem = ao2_iterator_next(&mem_iter))) {
06967             ast_str_set(&out, 0, "      %s", mem->membername);
06968             if (strcasecmp(mem->membername, mem->interface)) {
06969                ast_str_append(&out, 0, " (%s)", mem->interface);
06970             }
06971             if (mem->penalty)
06972                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
06973             ast_str_append(&out, 0, "%s%s%s (%s)",
06974                mem->dynamic ? " (dynamic)" : "",
06975                mem->realtime ? " (realtime)" : "",
06976                mem->paused ? " (paused)" : "",
06977                ast_devstate2str(mem->status));
06978             if (mem->calls)
06979                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
06980                   mem->calls, (long) (time(NULL) - mem->lastcall));
06981             else
06982                ast_str_append(&out, 0, " has taken no calls yet");
06983             do_print(s, fd, ast_str_buffer(out));
06984             ao2_ref(mem, -1);
06985          }
06986          ao2_iterator_destroy(&mem_iter);
06987       }
06988       if (!q->head)
06989          do_print(s, fd, "   No Callers");
06990       else {
06991          struct queue_ent *qe;
06992          int pos = 1;
06993 
06994          do_print(s, fd, "   Callers: ");
06995          for (qe = q->head; qe; qe = qe->next) {
06996             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
06997                pos++, qe->chan->name, (long) (now - qe->start) / 60,
06998                (long) (now - qe->start) % 60, qe->prio);
06999             do_print(s, fd, ast_str_buffer(out));
07000          }
07001       }
07002       do_print(s, fd, ""); /* blank line between entries */
07003       ao2_unlock(q);
07004       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
07005    }
07006    ao2_iterator_destroy(&queue_iter);
07007    ao2_unlock(queues);
07008    if (!found) {
07009       if (argc == 3)
07010          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
07011       else
07012          ast_str_set(&out, 0, "No queues.");
07013       do_print(s, fd, ast_str_buffer(out));
07014    }
07015    return CLI_SUCCESS;
07016 }

static void __reg_module ( void   )  [static]

Definition at line 8378 of file app_queue.c.

static void __unreg_module ( void   )  [static]

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

05211 {
05212    struct call_queue *q;
05213    struct member *new_member, *old_member;
05214    int res = RES_NOSUCHQUEUE;
05215 
05216    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
05217     * short-circuits if the queue is already in memory. */
05218    if (!(q = load_realtime_queue(queuename)))
05219       return res;
05220 
05221    ao2_lock(queues);
05222 
05223    ao2_lock(q);
05224    if ((old_member = interface_exists(q, interface)) == NULL) {
05225       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
05226          new_member->dynamic = 1;
05227          ao2_link(q->members, new_member);
05228          q->membercount++;
05229          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
05230             "Queue: %s\r\n"
05231             "Location: %s\r\n"
05232             "MemberName: %s\r\n"
05233             "Membership: %s\r\n"
05234             "Penalty: %d\r\n"
05235             "CallsTaken: %d\r\n"
05236             "LastCall: %d\r\n"
05237             "Status: %d\r\n"
05238             "Paused: %d\r\n",
05239             q->name, new_member->interface, new_member->membername,
05240             "dynamic",
05241             new_member->penalty, new_member->calls, (int) new_member->lastcall,
05242             new_member->status, new_member->paused);
05243          
05244          ao2_ref(new_member, -1);
05245          new_member = NULL;
05246 
05247          if (dump)
05248             dump_queue_members(q);
05249          
05250          res = RES_OKAY;
05251       } else {
05252          res = RES_OUTOFMEMORY;
05253       }
05254    } else {
05255       ao2_ref(old_member, -1);
05256       res = RES_EXISTS;
05257    }
05258    ao2_unlock(q);
05259    ao2_unlock(queues);
05260    queue_t_unref(q, "Expiring temporary reference");
05261 
05262    return res;
05263 }

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

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

02170 {
02171    struct call_queue *q;
02172 
02173    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
02174       if (ast_string_field_init(q, 64)) {
02175          queue_t_unref(q, "String field allocation failed");
02176          return NULL;
02177       }
02178       ast_string_field_set(q, name, queuename);
02179    }
02180    return q;
02181 }

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

AddQueueMember application.

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

05650 {
05651    int res=-1;
05652    char *parse, *temppos = NULL;
05653    AST_DECLARE_APP_ARGS(args,
05654       AST_APP_ARG(queuename);
05655       AST_APP_ARG(interface);
05656       AST_APP_ARG(penalty);
05657       AST_APP_ARG(options);
05658       AST_APP_ARG(membername);
05659       AST_APP_ARG(state_interface);
05660    );
05661    int penalty = 0;
05662 
05663    if (ast_strlen_zero(data)) {
05664       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
05665       return -1;
05666    }
05667 
05668    parse = ast_strdupa(data);
05669 
05670    AST_STANDARD_APP_ARGS(args, parse);
05671 
05672    if (ast_strlen_zero(args.interface)) {
05673       args.interface = ast_strdupa(chan->name);
05674       temppos = strrchr(args.interface, '-');
05675       if (temppos)
05676          *temppos = '\0';
05677    }
05678 
05679    if (!ast_strlen_zero(args.penalty)) {
05680       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
05681          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
05682          penalty = 0;
05683       }
05684    }
05685 
05686    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
05687    case RES_OKAY:
05688       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
05689       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
05690       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
05691       res = 0;
05692       break;
05693    case RES_EXISTS:
05694       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
05695       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
05696       res = 0;
05697       break;
05698    case RES_NOSUCHQUEUE:
05699       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
05700       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
05701       res = 0;
05702       break;
05703    case RES_OUTOFMEMORY:
05704       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
05705       break;
05706    }
05707 
05708    return res;
05709 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

04214 {
04215    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
04216 }

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

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

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

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

Calculate the metric of each member in the outgoing callattempts.

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

Return values:
-1 if penalties are exceeded
0 otherwise

Definition at line 4041 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, QUEUE_STRATEGY_RRORDERED, QUEUE_STRATEGY_WRANDOM, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

04042 {
04043    /* disregarding penalty on too few members? */
04044    unsigned char usepenalty = (q->membercount <= q->penaltymemberslimit) ? 0 : 1;
04045 
04046    if (usepenalty) {
04047       if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) ||
04048          (qe->min_penalty && (mem->penalty < qe->min_penalty))) {
04049          return -1;
04050       }
04051    } else {
04052       ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n",
04053            q->membercount, q->penaltymemberslimit);
04054    }
04055 
04056    switch (q->strategy) {
04057    case QUEUE_STRATEGY_RINGALL:
04058       /* Everyone equal, except for penalty */
04059       tmp->metric = mem->penalty * 1000000 * usepenalty;
04060       break;
04061    case QUEUE_STRATEGY_LINEAR:
04062       if (pos < qe->linpos) {
04063          tmp->metric = 1000 + pos;
04064       } else {
04065          if (pos > qe->linpos)
04066             /* Indicate there is another priority */
04067             qe->linwrapped = 1;
04068          tmp->metric = pos;
04069       }
04070       tmp->metric += mem->penalty * 1000000 * usepenalty;
04071       break;
04072    case QUEUE_STRATEGY_RRORDERED:
04073    case QUEUE_STRATEGY_RRMEMORY:
04074       if (pos < q->rrpos) {
04075          tmp->metric = 1000 + pos;
04076       } else {
04077          if (pos > q->rrpos)
04078             /* Indicate there is another priority */
04079             q->wrapped = 1;
04080          tmp->metric = pos;
04081       }
04082       tmp->metric += mem->penalty * 1000000 * usepenalty;
04083       break;
04084    case QUEUE_STRATEGY_RANDOM:
04085       tmp->metric = ast_random() % 1000;
04086       tmp->metric += mem->penalty * 1000000 * usepenalty;
04087       break;
04088    case QUEUE_STRATEGY_WRANDOM:
04089       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
04090       break;
04091    case QUEUE_STRATEGY_FEWESTCALLS:
04092       tmp->metric = mem->calls;
04093       tmp->metric += mem->penalty * 1000000 * usepenalty;
04094       break;
04095    case QUEUE_STRATEGY_LEASTRECENT:
04096       if (!mem->lastcall)
04097          tmp->metric = 0;
04098       else
04099          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
04100       tmp->metric += mem->penalty * 1000000 * usepenalty;
04101       break;
04102    default:
04103       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
04104       break;
04105    }
04106    return 0;
04107 }

static void callattempt_free ( struct callattempt doomed  )  [static]

Definition at line 2824 of file app_queue.c.

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

Referenced by hangupcalls().

02825 {
02826    if (doomed->member) {
02827       ao2_ref(doomed->member, -1);
02828    }
02829    ast_party_connected_line_free(&doomed->connected);
02830    ast_free(doomed);
02831 }

static void clear_queue ( struct call_queue q  )  [static]

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

01735 {
01736    q->holdtime = 0;
01737    q->callscompleted = 0;
01738    q->callsabandoned = 0;
01739    q->callscompletedinsl = 0;
01740    q->talktime = 0;
01741 
01742    if (q->members) {
01743       struct member *mem;
01744       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01745       while ((mem = ao2_iterator_next(&mem_iter))) {
01746          mem->calls = 0;
01747          mem->lastcall = 0;
01748          ao2_ref(mem, -1);
01749       }
01750       ao2_iterator_destroy(&mem_iter);
01751    }
01752 }

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

06832 {
06833    struct call_queue *q;
06834    struct ao2_iterator queue_iter = ao2_iterator_init(queues, 0);
06835    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06836       ao2_lock(q);
06837       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename))
06838          clear_queue(q);
06839       ao2_unlock(q);
06840       queue_t_unref(q, "Done with iterator");
06841    }
06842    ao2_iterator_destroy(&queue_iter);
06843    return 0;
06844 }

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

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

02905 {
02906    struct call_queue *q;
02907    struct member *mem;
02908    int found = 0;
02909    struct ao2_iterator queue_iter;
02910    
02911    /* q's lock and rq's lock already set by try_calling()
02912     * to solve deadlock */
02913    queue_iter = ao2_iterator_init(queues, 0);
02914    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
02915       if (q == rq) { /* don't check myself, could deadlock */
02916          queue_t_unref(q, "Done with iterator");
02917          continue;
02918       }
02919       ao2_lock(q);
02920       if (q->count && q->members) {
02921          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02922             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02923             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02924                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);
02925                found = 1;
02926             }
02927             ao2_ref(mem, -1);
02928          }
02929       }
02930       ao2_unlock(q);
02931       queue_t_unref(q, "Done with iterator");
02932       if (found) {
02933          break;
02934       }
02935    }
02936    ao2_iterator_destroy(&queue_iter);
02937    return found;
02938 }

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

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

07019 {
07020    struct call_queue *q;
07021    char *ret = NULL;
07022    int which = 0;
07023    int wordlen = strlen(word);
07024    struct ao2_iterator queue_iter;
07025 
07026    queue_iter = ao2_iterator_init(queues, 0);
07027    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07028       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
07029          ret = ast_strdup(q->name);
07030          queue_t_unref(q, "Done with iterator");
07031          break;
07032       }
07033       queue_t_unref(q, "Done with iterator");
07034    }
07035    ao2_iterator_destroy(&queue_iter);
07036 
07037    return ret;
07038 }

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

Definition at line 7450 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

Referenced by handle_queue_add_member().

07451 {
07452    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
07453    switch (pos) {
07454    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
07455       return NULL;
07456    case 4: /* only one possible match, "to" */
07457       return state == 0 ? ast_strdup("to") : NULL;
07458    case 5: /* <queue> */
07459       return complete_queue(line, word, pos, state);
07460    case 6: /* only one possible match, "penalty" */
07461       return state == 0 ? ast_strdup("penalty") : NULL;
07462    case 7:
07463       if (state < 100) {      /* 0-99 */
07464          char *num;
07465          if ((num = ast_malloc(3))) {
07466             sprintf(num, "%d", state);
07467          }
07468          return num;
07469       } else {
07470          return NULL;
07471       }
07472    case 8: /* only one possible match, "as" */
07473       return state == 0 ? ast_strdup("as") : NULL;
07474    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
07475       return NULL;
07476    default:
07477       return NULL;
07478    }
07479 }

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

Definition at line 7671 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

07672 {
07673    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
07674    switch (pos) {
07675    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
07676       return NULL;
07677    case 4:  /* only one possible match, "queue" */
07678       return state == 0 ? ast_strdup("queue") : NULL;
07679    case 5:  /* <queue> */
07680       return complete_queue(line, word, pos, state);
07681    case 6: /* "reason" */
07682       return state == 0 ? ast_strdup("reason") : NULL;
07683    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
07684       return NULL;
07685    default:
07686       return NULL;
07687    }
07688 }

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

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

07581 {
07582    int which = 0;
07583    struct call_queue *q;
07584    struct member *m;
07585    struct ao2_iterator queue_iter;
07586    struct ao2_iterator mem_iter;
07587    int wordlen = strlen(word);
07588 
07589    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
07590    if (pos > 5 || pos < 3)
07591       return NULL;
07592    if (pos == 4)   /* only one possible match, 'from' */
07593       return (state == 0 ? ast_strdup("from") : NULL);
07594 
07595    if (pos == 5)   /* No need to duplicate code */
07596       return complete_queue(line, word, pos, state);
07597 
07598    /* here is the case for 3, <member> */
07599    queue_iter = ao2_iterator_init(queues, 0);
07600    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07601       ao2_lock(q);
07602       mem_iter = ao2_iterator_init(q->members, 0);
07603       while ((m = ao2_iterator_next(&mem_iter))) {
07604          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
07605             char *tmp;
07606             ao2_unlock(q);
07607             tmp = ast_strdup(m->interface);
07608             ao2_ref(m, -1);
07609             queue_t_unref(q, "Done with iterator, returning interface name");
07610             ao2_iterator_destroy(&mem_iter);
07611             ao2_iterator_destroy(&queue_iter);
07612             return tmp;
07613          }
07614          ao2_ref(m, -1);
07615       }
07616       ao2_iterator_destroy(&mem_iter);
07617       ao2_unlock(q);
07618       queue_t_unref(q, "Done with iterator");
07619    }
07620    ao2_iterator_destroy(&queue_iter);
07621 
07622    return NULL;
07623 }

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

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

07805 {
07806    int which = 0;
07807    struct rule_list *rl_iter;
07808    int wordlen = strlen(word);
07809    char *ret = NULL;
07810    if (pos != 3) /* Wha? */ {
07811       return NULL;
07812    }
07813 
07814    AST_LIST_LOCK(&rule_lists);
07815    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07816       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
07817          ret = ast_strdup(rl_iter->name);
07818          break;
07819       }
07820    }
07821    AST_LIST_UNLOCK(&rule_lists);
07822 
07823    return ret;
07824 }

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

Definition at line 7741 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

07742 {
07743    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
07744    switch (pos) {
07745    case 4:
07746       if (state == 0) {
07747          return ast_strdup("on");
07748       } else {
07749          return NULL;
07750       }
07751    case 6:
07752       if (state == 0) {
07753          return ast_strdup("in");
07754       } else {
07755          return NULL;
07756       }
07757    case 7:
07758       return complete_queue(line, word, pos, state);
07759    default:
07760       return NULL;
07761    }
07762 }

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

Definition at line 7040 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

07041 {
07042    if (pos == 2)
07043       return complete_queue(line, word, pos, state);
07044    return NULL;
07045 }

static int compress_char ( const char  c  )  [static]

Definition at line 1631 of file app_queue.c.

Referenced by member_hash_fn().

01632 {
01633    if (c < 32)
01634       return 0;
01635    else if (c > 96)
01636       return c - 64;
01637    else
01638       return c - 32;
01639 }

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

Copy rule from global list into specified queue.

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

05747 {
05748    struct penalty_rule *pr_iter;
05749    struct rule_list *rl_iter;
05750    const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename;
05751    AST_LIST_LOCK(&rule_lists);
05752    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05753       if (!strcasecmp(rl_iter->name, tmp))
05754          break;
05755    }
05756    if (rl_iter) {
05757       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05758          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
05759          if (!new_pr) {
05760             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
05761             AST_LIST_UNLOCK(&rule_lists);
05762             break;
05763          }
05764          new_pr->time = pr_iter->time;
05765          new_pr->max_value = pr_iter->max_value;
05766          new_pr->min_value = pr_iter->min_value;
05767          new_pr->max_relative = pr_iter->max_relative;
05768          new_pr->min_relative = pr_iter->min_relative;
05769          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
05770       }
05771    }
05772    AST_LIST_UNLOCK(&rule_lists);
05773 }

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

01600 {
01601    struct member *cur;
01602    
01603    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
01604       cur->penalty = penalty;
01605       cur->paused = paused;
01606       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
01607       if (!ast_strlen_zero(state_interface))
01608          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
01609       else
01610          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
01611       if (!ast_strlen_zero(membername))
01612          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
01613       else
01614          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
01615       if (!strchr(cur->interface, '/'))
01616          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
01617       if (!strncmp(cur->state_interface, "hint:", 5)) {
01618          char *tmp = ast_strdupa(cur->state_interface), *context = tmp;
01619          char *exten = strsep(&context, "@") + 5;
01620 
01621          ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten));
01622          ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context));
01623       }
01624       cur->status = get_queue_member_status(cur);
01625    }
01626 
01627    return cur;
01628 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

02156 {
02157    struct call_queue *q = obj;
02158    int i;
02159 
02160    free_members(q, 1);
02161    ast_string_field_free_memory(q);
02162    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
02163       if (q->sound_periodicannounce[i])
02164          free(q->sound_periodicannounce[i]);
02165    }
02166    ao2_ref(q->members, -1);
02167 }

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

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

01498 {
01499    enum ast_device_state state;
01500    const char *device;
01501    struct statechange *sc;
01502    size_t datapsize;
01503 
01504    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
01505    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
01506 
01507    if (ast_strlen_zero(device)) {
01508       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
01509       return;
01510    }
01511    datapsize = sizeof(*sc) + strlen(device) + 1;
01512    if (!(sc = ast_calloc(1, datapsize))) {
01513       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
01514       return;
01515    }
01516    sc->state = state;
01517    strcpy(sc->dev, device);
01518    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
01519       ast_free(sc);
01520    }
01521 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2941 of file app_queue.c.

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

Referenced by ring_entry().

02942 {
02943    o->stillgoing = 0;
02944    ast_hangup(o->chan);
02945    o->chan = NULL;
02946 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

06877 {
06878    if (s)
06879       astman_append(s, "%s\r\n", str);
06880    else
06881       ast_cli(fd, "%s\n", str);
06882 }

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

05112 {
05113    struct member *cur_member;
05114    char value[PM_MAX_LEN];
05115    int value_len = 0;
05116    int res;
05117    struct ao2_iterator mem_iter;
05118 
05119    memset(value, 0, sizeof(value));
05120 
05121    if (!pm_queue)
05122       return;
05123 
05124    mem_iter = ao2_iterator_init(pm_queue->members, 0);
05125    while ((cur_member = ao2_iterator_next(&mem_iter))) {
05126       if (!cur_member->dynamic) {
05127          ao2_ref(cur_member, -1);
05128          continue;
05129       }
05130 
05131       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
05132          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
05133 
05134       ao2_ref(cur_member, -1);
05135 
05136       if (res != strlen(value + value_len)) {
05137          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
05138          break;
05139       }
05140       value_len += res;
05141    }
05142    ao2_iterator_destroy(&mem_iter);
05143    
05144    if (value_len && !cur_member) {
05145       if (ast_db_put(pm_family, pm_queue->name, value))
05146          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
05147    } else
05148       /* Delete the entry if the queue is empty or there is an error */
05149       ast_db_del(pm_family, pm_queue->name);
05150 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 4260 of file app_queue.c.

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

04261 {
04262    struct queue_end_bridge *qeb = data;
04263    struct call_queue *q = qeb->q;
04264    struct ast_channel *chan = qeb->chan;
04265 
04266    if (ao2_ref(qeb, -1) == 1) {
04267       set_queue_variables(q, chan);
04268       /* This unrefs the reference we made in try_calling when we allocated qeb */
04269       queue_t_unref(q, "Expire bridge_config reference");
04270    }
04271 }

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

Definition at line 4253 of file app_queue.c.

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

04254 {
04255    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
04256    ao2_ref(qeb, +1);
04257    qeb->chan = originator;
04258 }

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

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

01556 {
01557    struct ao2_iterator miter, qiter;
01558    struct member *m;
01559    struct call_queue *q;
01560    int found = 0, device_state = extensionstate2devicestate(state);
01561 
01562    qiter = ao2_iterator_init(queues, 0);
01563    while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) {
01564       ao2_lock(q);
01565 
01566       miter = ao2_iterator_init(q->members, 0);
01567       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01568          if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) {
01569             update_status(q, m, device_state);
01570             ao2_ref(m, -1);
01571             found = 1;
01572             break;
01573          }
01574       }
01575       ao2_iterator_destroy(&miter);
01576 
01577       ao2_unlock(q);
01578       queue_t_unref(q, "Done with iterator");
01579    }
01580    ao2_iterator_destroy(&qiter);
01581 
01582         if (found) {
01583       ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state));
01584    } else {
01585       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",
01586            exten, context, device_state, ast_devstate2str(device_state));
01587    }
01588 
01589    return 0;
01590 }

static int extensionstate2devicestate ( int  state  )  [static]

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

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

01525 {
01526    switch (state) {
01527    case AST_EXTENSION_NOT_INUSE:
01528       state = AST_DEVICE_NOT_INUSE;
01529       break;
01530    case AST_EXTENSION_INUSE:
01531       state = AST_DEVICE_INUSE;
01532       break;
01533    case AST_EXTENSION_BUSY:
01534       state = AST_DEVICE_BUSY;
01535       break;
01536    case AST_EXTENSION_RINGING:
01537       state = AST_DEVICE_RINGING;
01538       break;
01539    case AST_EXTENSION_ONHOLD:
01540       state = AST_DEVICE_ONHOLD;
01541       break;
01542    case AST_EXTENSION_UNAVAILABLE:
01543       state = AST_DEVICE_UNAVAILABLE;
01544       break;
01545    case AST_EXTENSION_REMOVED:
01546    case AST_EXTENSION_DEACTIVATED:
01547    default:
01548       state = AST_DEVICE_INVALID;
01549       break;
01550    }
01551 
01552    return state;
01553 }

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

find the entry with the best metric, or NULL

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

03176 {
03177    struct callattempt *best = NULL, *cur;
03178 
03179    for (cur = outgoing; cur; cur = cur->q_next) {
03180       if (cur->stillgoing &&              /* Not already done */
03181          !cur->chan &&              /* Isn't already going */
03182          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
03183          best = cur;
03184       }
03185    }
03186 
03187    return best;
03188 }

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

02194 {
02195    struct ast_variable *v;
02196    struct call_queue *q, tmpq = {
02197       .name = queuename,   
02198    };
02199    struct member *m;
02200    struct ao2_iterator mem_iter;
02201    char *interface = NULL;
02202    const char *tmp_name;
02203    char *tmp;
02204    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
02205 
02206    /* Static queues override realtime. */
02207    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
02208       ao2_lock(q);
02209       if (!q->realtime) {
02210          if (q->dead) {
02211             ao2_unlock(q);
02212             queue_t_unref(q, "Queue is dead; can't return it");
02213             return NULL;
02214          } else {
02215             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
02216             ao2_unlock(q);
02217             return q;
02218          }
02219       }
02220    } else if (!member_config)
02221       /* Not found in the list, and it's not realtime ... */
02222       return NULL;
02223 
02224    /* Check if queue is defined in realtime. */
02225    if (!queue_vars) {
02226       /* Delete queue from in-core list if it has been deleted in realtime. */
02227       if (q) {
02228          /*! \note Hmm, can't seem to distinguish a DB failure from a not
02229             found condition... So we might delete an in-core queue
02230             in case of DB failure. */
02231          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
02232 
02233          q->dead = 1;
02234          /* Delete if unused (else will be deleted when last caller leaves). */
02235          queues_t_unlink(queues, q, "Unused; removing from container");
02236          ao2_unlock(q);
02237          queue_t_unref(q, "Queue is dead; can't return it");
02238       }
02239       return NULL;
02240    }
02241 
02242    /* Create a new queue if an in-core entry does not exist yet. */
02243    if (!q) {
02244       struct ast_variable *tmpvar = NULL;
02245       if (!(q = alloc_queue(queuename)))
02246          return NULL;
02247       ao2_lock(q);
02248       clear_queue(q);
02249       q->realtime = 1;
02250       q->membercount = 0;
02251       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
02252        * will allocate the members properly
02253        */
02254       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
02255          if (!strcasecmp(tmpvar->name, "strategy")) {
02256             q->strategy = strat2int(tmpvar->value);
02257             if (q->strategy < 0) {
02258                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02259                tmpvar->value, q->name);
02260                q->strategy = QUEUE_STRATEGY_RINGALL;
02261             }
02262             break;
02263          }
02264       }
02265       /* We traversed all variables and didn't find a strategy */
02266       if (!tmpvar)
02267          q->strategy = QUEUE_STRATEGY_RINGALL;
02268       queues_t_link(queues, q, "Add queue to container");
02269    }
02270    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
02271 
02272    memset(tmpbuf, 0, sizeof(tmpbuf));
02273    for (v = queue_vars; v; v = v->next) {
02274       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
02275       if ((tmp = strchr(v->name, '_'))) {
02276          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
02277          tmp_name = tmpbuf;
02278          tmp = tmpbuf;
02279          while ((tmp = strchr(tmp, '_')))
02280             *tmp++ = '-';
02281       } else
02282          tmp_name = v->name;
02283 
02284       /* NULL values don't get returned from realtime; blank values should
02285        * still get set.  If someone doesn't want a value to be set, they
02286        * should set the realtime column to NULL, not blank. */
02287       queue_set_param(q, tmp_name, v->value, -1, 0);
02288    }
02289 
02290    /* Temporarily set realtime members dead so we can detect deleted ones. 
02291     * Also set the membercount correctly for realtime*/
02292    mem_iter = ao2_iterator_init(q->members, 0);
02293    while ((m = ao2_iterator_next(&mem_iter))) {
02294       q->membercount++;
02295       if (m->realtime)
02296          m->dead = 1;
02297       ao2_ref(m, -1);
02298    }
02299    ao2_iterator_destroy(&mem_iter);
02300 
02301    while ((interface = ast_category_browse(member_config, interface))) {
02302       rt_handle_member_record(q, interface,
02303          ast_variable_retrieve(member_config, interface, "uniqueid"),
02304          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
02305          ast_variable_retrieve(member_config, interface, "penalty"),
02306          ast_variable_retrieve(member_config, interface, "paused"),
02307          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
02308    }
02309 
02310    /* Delete all realtime members that have been deleted in DB. */
02311    mem_iter = ao2_iterator_init(q->members, 0);
02312    while ((m = ao2_iterator_next(&mem_iter))) {
02313       if (m->dead) {
02314          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02315          ao2_unlink(q->members, m);
02316          q->membercount--;
02317       }
02318       ao2_ref(m, -1);
02319    }
02320    ao2_iterator_destroy(&mem_iter);
02321 
02322    ao2_unlock(q);
02323 
02324    return q;
02325 }

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

Iterate through queue's member list and delete them.

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

02139 {
02140    /* Free non-dynamic members */
02141    struct member *cur;
02142    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
02143 
02144    while ((cur = ao2_iterator_next(&mem_iter))) {
02145       if (all || !cur->dynamic) {
02146          ao2_unlink(q->members, cur);
02147          q->membercount--;
02148       }
02149       ao2_ref(cur, -1);
02150    }
02151    ao2_iterator_destroy(&mem_iter);
02152 }

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

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

05392 {
05393    int foundqueue = 0, penalty;
05394    struct call_queue *q, tmpq = {
05395       .name = queuename,   
05396    };
05397    struct member *mem;
05398    
05399    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
05400       foundqueue = 1;
05401       ao2_lock(q);
05402       if ((mem = interface_exists(q, interface))) {
05403          penalty = mem->penalty;
05404          ao2_ref(mem, -1);
05405          ao2_unlock(q);
05406          queue_t_unref(q, "Search complete");
05407          return penalty;
05408       }
05409       ao2_unlock(q);
05410       queue_t_unref(q, "Search complete");
05411    }
05412 
05413    /* some useful debuging */
05414    if (foundqueue) 
05415       ast_log (LOG_ERROR, "Invalid queuename\n");
05416    else 
05417       ast_log (LOG_ERROR, "Invalid interface\n");
05418 
05419    return RESULT_FAILURE;
05420 }

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

01348 {
01349    struct member *member;
01350    struct ao2_iterator mem_iter;
01351 
01352    ao2_lock(q);
01353    mem_iter = ao2_iterator_init(q->members, 0);
01354    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
01355       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) {
01356          if (conditions & QUEUE_EMPTY_PENALTY) {
01357             ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty);
01358             continue;
01359          }
01360       }
01361 
01362       switch (member->status) {
01363       case AST_DEVICE_INVALID:
01364          if (conditions & QUEUE_EMPTY_INVALID) {
01365             ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername);
01366             break;
01367          }
01368          goto default_case;
01369       case AST_DEVICE_UNAVAILABLE:
01370          if (conditions & QUEUE_EMPTY_UNAVAILABLE) {
01371             ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername);
01372             break;
01373          }
01374          goto default_case;
01375       case AST_DEVICE_INUSE:
01376          if (conditions & QUEUE_EMPTY_INUSE) {
01377             ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername);
01378             break;
01379          }
01380          goto default_case;
01381       case AST_DEVICE_RINGING:
01382          if (conditions & QUEUE_EMPTY_RINGING) {
01383             ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername);
01384             break;
01385          }
01386          goto default_case;
01387       case AST_DEVICE_UNKNOWN:
01388          if (conditions & QUEUE_EMPTY_UNKNOWN) {
01389             ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername);
01390             break;
01391          }
01392          /* Fall-through */
01393       default:
01394       default_case:
01395          if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) {
01396             ast_debug(4, "%s is unavailable because he is paused'\n", member->membername);
01397             break;
01398          } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) {
01399             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);
01400             break;
01401          } else {
01402             ao2_unlock(q);
01403             ao2_ref(member, -1);
01404             ao2_iterator_destroy(&mem_iter);
01405             ast_debug(4, "%s is available.\n", member->membername);
01406             return 0;
01407          }
01408          break;
01409       }
01410    }
01411    ao2_iterator_destroy(&mem_iter);
01412 
01413    ao2_unlock(q);
01414    return -1;
01415 }

static int get_queue_member_status ( struct member cur  )  [static]

Return the current state of a member.

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

01594 {
01595    return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten));
01596 }

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

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

07507 {
07508    const char *queuename, *interface, *membername = NULL, *state_interface = NULL;
07509    int penalty;
07510 
07511    switch ( cmd ) {
07512    case CLI_INIT:
07513       e->command = "queue add member";
07514       e->usage =
07515          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
07516          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
07517       return NULL;
07518    case CLI_GENERATE:
07519       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
07520    }
07521 
07522    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
07523       return CLI_SHOWUSAGE;
07524    } else if (strcmp(a->argv[4], "to")) {
07525       return CLI_SHOWUSAGE;
07526    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
07527       return CLI_SHOWUSAGE;
07528    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
07529       return CLI_SHOWUSAGE;
07530    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
07531       return CLI_SHOWUSAGE;
07532    }
07533 
07534    queuename = a->argv[5];
07535    interface = a->argv[3];
07536    if (a->argc >= 8) {
07537       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
07538          if (penalty < 0) {
07539             ast_cli(a->fd, "Penalty must be >= 0\n");
07540             penalty = 0;
07541          }
07542       } else {
07543          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
07544          penalty = 0;
07545       }
07546    } else {
07547       penalty = 0;
07548    }
07549 
07550    if (a->argc >= 10) {
07551       membername = a->argv[9];
07552    }
07553 
07554    if (a->argc >= 12) {
07555       state_interface = a->argv[11];
07556    }
07557 
07558    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
07559    case RES_OKAY:
07560       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
07561       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
07562       return CLI_SUCCESS;
07563    case RES_EXISTS:
07564       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
07565       return CLI_FAILURE;
07566    case RES_NOSUCHQUEUE:
07567       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
07568       return CLI_FAILURE;
07569    case RES_OUTOFMEMORY:
07570       ast_cli(a->fd, "Out of memory\n");
07571       return CLI_FAILURE;
07572    case RES_NOT_DYNAMIC:
07573       ast_cli(a->fd, "Member not dynamic\n");
07574       return CLI_FAILURE;
07575    default:
07576       return CLI_FAILURE;
07577    }
07578 }

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

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

07691 {
07692    const char *queuename, *interface, *reason;
07693    int paused;
07694 
07695    switch (cmd) {
07696    case CLI_INIT:
07697       e->command = "queue {pause|unpause} member";
07698       e->usage = 
07699          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
07700          "  Pause or unpause a queue member. Not specifying a particular queue\n"
07701          "  will pause or unpause a member across all queues to which the member\n"
07702          "  belongs.\n";
07703       return NULL;
07704    case CLI_GENERATE:
07705       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
07706    }
07707 
07708    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
07709       return CLI_SHOWUSAGE;
07710    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
07711       return CLI_SHOWUSAGE;
07712    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
07713       return CLI_SHOWUSAGE;
07714    }
07715 
07716 
07717    interface = a->argv[3];
07718    queuename = a->argc >= 6 ? a->argv[5] : NULL;
07719    reason = a->argc == 8 ? a->argv[7] : NULL;
07720    paused = !strcasecmp(a->argv[1], "pause");
07721 
07722    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
07723       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
07724       if (!ast_strlen_zero(queuename))
07725          ast_cli(a->fd, " in queue '%s'", queuename);
07726       if (!ast_strlen_zero(reason))
07727          ast_cli(a->fd, " for reason '%s'", reason);
07728       ast_cli(a->fd, "\n");
07729       return CLI_SUCCESS;
07730    } else {
07731       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
07732       if (!ast_strlen_zero(queuename))
07733          ast_cli(a->fd, " in queue '%s'", queuename);
07734       if (!ast_strlen_zero(reason))
07735          ast_cli(a->fd, " for reason '%s'", reason);
07736       ast_cli(a->fd, "\n");
07737       return CLI_FAILURE;
07738    }
07739 }

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

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

07900 {
07901    struct ast_flags mask = {0,};
07902    int i;
07903 
07904    switch (cmd) {
07905       case CLI_INIT:
07906          e->command = "queue reload {parameters|members|rules|all}";
07907          e->usage =
07908             "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n"
07909             "Reload queues. If <queuenames> are specified, only reload information pertaining\n"
07910             "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n"
07911             "specified in order to know what information to reload. Below is an explanation\n"
07912             "of each of these qualifiers.\n"
07913             "\n"
07914             "\t'members' - reload queue members from queues.conf\n"
07915             "\t'parameters' - reload all queue options except for queue members\n"
07916             "\t'rules' - reload the queuerules.conf file\n"
07917             "\t'all' - reload queue rules, parameters, and members\n"
07918             "\n"
07919             "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n"
07920             "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n"
07921             "one queue is specified when using this command, reloading queue rules may cause\n"
07922             "other queues to be affected\n";
07923          return NULL;
07924       case CLI_GENERATE:
07925          if (a->pos >= 3) {
07926             return complete_queue(a->line, a->word, a->pos, a->n);
07927          } else {
07928             return NULL;
07929          }
07930    }
07931 
07932    if (a->argc < 3)
07933       return CLI_SHOWUSAGE;
07934 
07935    if (!strcasecmp(a->argv[2], "rules")) {
07936       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07937    } else if (!strcasecmp(a->argv[2], "members")) {
07938       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07939    } else if (!strcasecmp(a->argv[2], "parameters")) {
07940       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07941    } else if (!strcasecmp(a->argv[2], "all")) {
07942       ast_set_flag(&mask, AST_FLAGS_ALL);
07943    }
07944 
07945    if (a->argc == 3) {
07946       reload_handler(1, &mask, NULL);
07947       return CLI_SUCCESS;
07948    }
07949 
07950    for (i = 3; i < a->argc; ++i) {
07951       reload_handler(1, &mask, a->argv[i]);
07952    }
07953 
07954    return CLI_SUCCESS;
07955 }

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

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

07626 {
07627    const char *queuename, *interface;
07628 
07629    switch (cmd) {
07630    case CLI_INIT:
07631       e->command = "queue remove member";
07632       e->usage = 
07633          "Usage: queue remove member <channel> from <queue>\n"
07634          "       Remove a specific channel from a queue.\n";
07635       return NULL;
07636    case CLI_GENERATE:
07637       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
07638    }
07639 
07640    if (a->argc != 6) {
07641       return CLI_SHOWUSAGE;
07642    } else if (strcmp(a->argv[4], "from")) {
07643       return CLI_SHOWUSAGE;
07644    }
07645 
07646    queuename = a->argv[5];
07647    interface = a->argv[3];
07648 
07649    switch (remove_from_queue(queuename, interface)) {
07650    case RES_OKAY:
07651       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
07652       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
07653       return CLI_SUCCESS;
07654    case RES_EXISTS:
07655       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
07656       return CLI_FAILURE;
07657    case RES_NOSUCHQUEUE:
07658       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
07659       return CLI_FAILURE;
07660    case RES_OUTOFMEMORY:
07661       ast_cli(a->fd, "Out of memory\n");
07662       return CLI_FAILURE;
07663    case RES_NOT_DYNAMIC:
07664       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename);
07665       return CLI_FAILURE;
07666    default:
07667       return CLI_FAILURE;
07668    }
07669 }

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

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

07861 {
07862    struct ast_flags mask = {QUEUE_RESET_STATS,};
07863    int i;
07864 
07865    switch (cmd) {
07866       case CLI_INIT:
07867          e->command = "queue reset stats";
07868          e->usage =
07869             "Usage: queue reset stats [<queuenames>]\n"
07870             "\n"
07871             "Issuing this command will reset statistics for\n"
07872             "<queuenames>, or for all queues if no queue is\n"
07873             "specified.\n";
07874          return NULL;
07875       case CLI_GENERATE:
07876          if (a->pos >= 3) {
07877             return complete_queue(a->line, a->word, a->pos, a->n);
07878          } else {
07879             return NULL;
07880          }
07881    }
07882 
07883    if (a->argc < 3) {
07884       return CLI_SHOWUSAGE;
07885    }
07886 
07887    if (a->argc == 3) {
07888       reload_handler(1, &mask, NULL);
07889       return CLI_SUCCESS;
07890    }
07891 
07892    for (i = 3; i < a->argc; ++i) {
07893       reload_handler(1, &mask, a->argv[i]);
07894    }
07895 
07896    return CLI_SUCCESS;
07897 }

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

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

07827 {
07828    const char *rule;
07829    struct rule_list *rl_iter;
07830    struct penalty_rule *pr_iter;
07831    switch (cmd) {
07832    case CLI_INIT:
07833       e->command = "queue show rules";
07834       e->usage =
07835       "Usage: queue show rules [rulename]\n"
07836       "  Show the list of rules associated with rulename. If no\n"
07837       "  rulename is specified, list all rules defined in queuerules.conf\n";
07838       return NULL;
07839    case CLI_GENERATE:
07840       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
07841    }
07842 
07843    if (a->argc != 3 && a->argc != 4)
07844       return CLI_SHOWUSAGE;
07845 
07846    rule = a->argc == 4 ? a->argv[3] : "";
07847    AST_LIST_LOCK(&rule_lists);
07848    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07849       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
07850          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
07851          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07852             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);
07853          }
07854       }
07855    }
07856    AST_LIST_UNLOCK(&rule_lists);
07857    return CLI_SUCCESS; 
07858 }

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

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

07765 {
07766    const char *queuename = NULL, *interface;
07767    int penalty = 0;
07768 
07769    switch (cmd) {
07770    case CLI_INIT:
07771       e->command = "queue set penalty";
07772       e->usage = 
07773       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
07774       "  Set a member's penalty in the queue specified. If no queue is specified\n"
07775       "  then that interface's penalty is set in all queues to which that interface is a member\n";
07776       return NULL;
07777    case CLI_GENERATE:
07778       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
07779    }
07780 
07781    if (a->argc != 6 && a->argc != 8) {
07782       return CLI_SHOWUSAGE;
07783    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
07784       return CLI_SHOWUSAGE;
07785    }
07786 
07787    if (a->argc == 8)
07788       queuename = a->argv[7];
07789    interface = a->argv[5];
07790    penalty = atoi(a->argv[3]);
07791 
07792    switch (set_member_penalty(queuename, interface, penalty)) {
07793    case RESULT_SUCCESS:
07794       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07795       return CLI_SUCCESS;
07796    case RESULT_FAILURE:
07797       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
07798       return CLI_FAILURE;
07799    default:
07800       return CLI_FAILURE;
07801    }
07802 }

static int handle_statechange ( void *  datap  )  [static]

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

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

01454 {
01455    struct statechange *sc = datap;
01456    struct ao2_iterator miter, qiter;
01457    struct member *m;
01458    struct call_queue *q;
01459    char interface[80], *slash_pos;
01460    int found = 0;
01461 
01462    qiter = ao2_iterator_init(queues, 0);
01463    while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) {
01464       ao2_lock(q);
01465 
01466       miter = ao2_iterator_init(q->members, 0);
01467       for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) {
01468          ast_copy_string(interface, m->state_interface, sizeof(interface));
01469 
01470          if ((slash_pos = strchr(interface, '/')))
01471             if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/')))
01472                *slash_pos = '\0';
01473 
01474          if (!strcasecmp(interface, sc->dev)) {
01475             found = 1;
01476             update_status(q, m, sc->state);
01477             ao2_ref(m, -1);
01478             break;
01479          }
01480       }
01481       ao2_iterator_destroy(&miter);
01482 
01483       ao2_unlock(q);
01484       queue_t_unref(q, "Done with iterator");
01485    }
01486    ao2_iterator_destroy(&qiter);
01487 
01488    if (found)
01489       ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state));
01490    else
01491       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));
01492 
01493    ast_free(sc);
01494    return 0;
01495 }

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

02835 {
02836    struct callattempt *oo;
02837 
02838    while (outgoing) {
02839       /* If someone else answered the call we should indicate this in the CANCEL */
02840       /* Hangup any existing lines we have open */
02841       if (outgoing->chan && (outgoing->chan != exception)) {
02842          if (exception || cancel_answered_elsewhere)
02843             ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE);
02844          ast_hangup(outgoing->chan);
02845       }
02846       oo = outgoing;
02847       outgoing = outgoing->q_next;
02848       ast_aoc_destroy_decoded(oo->aoc_s_rate_list);
02849       callattempt_free(oo);
02850    }
02851 }

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 1663 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, QUEUE_STRATEGY_RRORDERED, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_single_queue().

01664 {
01665    int i;
01666    struct penalty_rule *pr_iter;
01667 
01668    q->dead = 0;
01669    q->retry = DEFAULT_RETRY;
01670    q->timeout = DEFAULT_TIMEOUT;
01671    q->maxlen = 0;
01672    q->announcefrequency = 0;
01673    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
01674    q->announceholdtime = 1;
01675    q->announcepositionlimit = 10; /* Default 10 positions */
01676    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
01677    q->roundingseconds = 0; /* Default - don't announce seconds */
01678    q->servicelevel = 0;
01679    q->ringinuse = 1;
01680    q->setinterfacevar = 0;
01681    q->setqueuevar = 0;
01682    q->setqueueentryvar = 0;
01683    q->autofill = autofill_default;
01684    q->montype = montype_default;
01685    q->monfmt[0] = '\0';
01686    q->reportholdtime = 0;
01687    q->wrapuptime = 0;
01688    q->penaltymemberslimit = 0;
01689    q->joinempty = 0;
01690    q->leavewhenempty = 0;
01691    q->memberdelay = 0;
01692    q->maskmemberstatus = 0;
01693    q->eventwhencalled = 0;
01694    q->weight = 0;
01695    q->timeoutrestart = 0;
01696    q->periodicannouncefrequency = 0;
01697    q->randomperiodicannounce = 0;
01698    q->numperiodicannounce = 0;
01699    q->autopause = QUEUE_AUTOPAUSE_OFF;
01700    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01701    if (!q->members) {
01702       if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
01703          /* linear strategy depends on order, so we have to place all members in a single bucket */
01704          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
01705       else
01706          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
01707    }
01708    q->found = 1;
01709 
01710    ast_string_field_set(q, sound_next, "queue-youarenext");
01711    ast_string_field_set(q, sound_thereare, "queue-thereare");
01712    ast_string_field_set(q, sound_calls, "queue-callswaiting");
01713    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
01714    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
01715    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
01716    ast_string_field_set(q, sound_minutes, "queue-minutes");
01717    ast_string_field_set(q, sound_minute, "queue-minute");
01718    ast_string_field_set(q, sound_seconds, "queue-seconds");
01719    ast_string_field_set(q, sound_thanks, "queue-thankyou");
01720    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
01721 
01722    if ((q->sound_periodicannounce[0] = ast_str_create(32)))
01723       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
01724 
01725    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01726       if (q->sound_periodicannounce[i])
01727          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
01728    }
01729 
01730    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
01731       ast_free(pr_iter);
01732 }

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

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

Referenced by join_queue().

01318 {
01319    struct queue_ent *cur;
01320 
01321    if (!q || !new)
01322       return;
01323    if (prev) {
01324       cur = prev->next;
01325       prev->next = new;
01326    } else {
01327       cur = q->head;
01328       q->head = new;
01329    }
01330    new->next = cur;
01331 
01332    /* every queue_ent must have a reference to it's parent call_queue, this
01333     * reference does not go away until the end of the queue_ent's life, meaning
01334     * that even when the queue_ent leaves the call_queue this ref must remain. */
01335    queue_ref(q);
01336    new->parent = q;
01337    new->pos = ++(*pos);
01338    new->opos = *pos;
01339 }

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

01764 {
01765    char *timestr, *maxstr, *minstr, *contentdup;
01766    struct penalty_rule *rule = NULL, *rule_iter;
01767    struct rule_list *rl_iter;
01768    int penaltychangetime, inserted = 0;
01769 
01770    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01771       return -1;
01772    }
01773 
01774    contentdup = ast_strdupa(content);
01775    
01776    if (!(maxstr = strchr(contentdup, ','))) {
01777       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01778       ast_free(rule);
01779       return -1;
01780    }
01781 
01782    *maxstr++ = '\0';
01783    timestr = contentdup;
01784 
01785    if ((penaltychangetime = atoi(timestr)) < 0) {
01786       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01787       ast_free(rule);
01788       return -1;
01789    }
01790 
01791    rule->time = penaltychangetime;
01792 
01793    if ((minstr = strchr(maxstr,',')))
01794       *minstr++ = '\0';
01795    
01796    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01797     * OR if a min penalty change is indicated but no max penalty change is */
01798    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01799       rule->max_relative = 1;
01800    }
01801 
01802    rule->max_value = atoi(maxstr);
01803 
01804    if (!ast_strlen_zero(minstr)) {
01805       if (*minstr == '+' || *minstr == '-')
01806          rule->min_relative = 1;
01807       rule->min_value = atoi(minstr);
01808    } else /*there was no minimum specified, so assume this means no change*/
01809       rule->min_relative = 1;
01810 
01811    /*We have the rule made, now we need to insert it where it belongs*/
01812    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01813       if (strcasecmp(rl_iter->name, list_name))
01814          continue;
01815 
01816       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01817          if (rule->time < rule_iter->time) {
01818             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01819             inserted = 1;
01820             break;
01821          }
01822       }
01823       AST_LIST_TRAVERSE_SAFE_END;
01824    
01825       if (!inserted) {
01826          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01827       }
01828    }
01829 
01830    return 0;
01831 }

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

Definition at line 1209 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

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

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

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

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

05086 {
05087    struct member *mem;
05088    struct ao2_iterator mem_iter;
05089 
05090    if (!q)
05091       return NULL;
05092 
05093    mem_iter = ao2_iterator_init(q->members, 0);
05094    while ((mem = ao2_iterator_next(&mem_iter))) {
05095       if (!strcasecmp(interface, mem->interface)) {
05096          ao2_iterator_destroy(&mem_iter);
05097          return mem;
05098       }
05099       ao2_ref(mem, -1);
05100    }
05101    ao2_iterator_destroy(&mem_iter);
05102 
05103    return NULL;
05104 }

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

03843 {
03844    struct queue_ent *ch;
03845    int res;
03846    int avl;
03847    int idx = 0;
03848    /* This needs a lock. How many members are available to be served? */
03849    ao2_lock(qe->parent);
03850 
03851    avl = num_available_members(qe->parent);
03852 
03853    ch = qe->parent->head;
03854 
03855    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
03856 
03857    while ((idx < avl) && (ch) && (ch != qe)) {
03858       if (!ch->pending)
03859          idx++;
03860       ch = ch->next;       
03861    }
03862 
03863    ao2_unlock(qe->parent);
03864    /* If the queue entry is within avl [the number of available members] calls from the top ... 
03865     * Autofill and position check added to support autofill=no (as only calls
03866     * from the front of the queue are valid when autofill is disabled)
03867     */
03868    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
03869       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
03870       res = 1;
03871    } else {
03872       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
03873       res = 0;
03874    }
03875 
03876    return res;
03877 }

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

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

02454 {
02455    struct call_queue *q;
02456    struct queue_ent *cur, *prev = NULL;
02457    int res = -1;
02458    int pos = 0;
02459    int inserted = 0;
02460 
02461    if (!(q = load_realtime_queue(queuename)))
02462       return res;
02463 
02464    ao2_lock(queues);
02465    ao2_lock(q);
02466 
02467    /* This is our one */
02468    if (q->joinempty) {
02469       int status = 0;
02470       if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) {
02471          *reason = QUEUE_JOINEMPTY;
02472          ao2_unlock(q);
02473          ao2_unlock(queues);
02474          return res;
02475       }
02476    }
02477    if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen))
02478       *reason = QUEUE_FULL;
02479    else if (*reason == QUEUE_UNKNOWN) {
02480       /* There's space for us, put us at the right position inside
02481        * the queue.
02482        * Take into account the priority of the calling user */
02483       inserted = 0;
02484       prev = NULL;
02485       cur = q->head;
02486       while (cur) {
02487          /* We have higher priority than the current user, enter
02488           * before him, after all the other users with priority
02489           * higher or equal to our priority. */
02490          if ((!inserted) && (qe->prio > cur->prio)) {
02491             insert_entry(q, prev, qe, &pos);
02492             inserted = 1;
02493          }
02494          /* <= is necessary for the position comparison because it may not be possible to enter
02495           * at our desired position since higher-priority callers may have taken the position we want
02496           */
02497          if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) {
02498             insert_entry(q, prev, qe, &pos);
02499             /*pos is incremented inside insert_entry, so don't need to add 1 here*/
02500             if (position < pos) {
02501                ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos);
02502             }
02503             inserted = 1;
02504          }
02505          cur->pos = ++pos;
02506          prev = cur;
02507          cur = cur->next;
02508       }
02509       /* No luck, join at the end of the queue */
02510       if (!inserted)
02511          insert_entry(q, prev, qe, &pos);
02512       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
02513       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
02514       ast_copy_string(qe->context, q->context, sizeof(qe->context));
02515       q->count++;
02516       res = 0;
02517       ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join",
02518          "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
02519          qe->chan->name,
02520          S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */
02521          S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
02522          q->name, qe->pos, q->count, qe->chan->uniqueid );
02523       ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
02524    }
02525    ao2_unlock(q);
02526    ao2_unlock(queues);
02527 
02528    return res;
02529 }

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

Definition at line 6610 of file app_queue.c.

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

06611 {
06612    struct member *member = obj;
06613    struct call_queue *q = arg;
06614 
06615    if (!member->delme) {
06616       if (member->dynamic) {
06617          /* dynamic members were not counted toward the member count
06618           * when reloading members from queues.conf, so we do that here
06619           */
06620          q->membercount++;
06621       }
06622       member->status = get_queue_member_status(member);
06623       return 0;
06624    } else {
06625       q->membercount--;
06626       return CMP_MATCH;
06627    }
06628 }

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

Definition at line 6748 of file app_queue.c.

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

Referenced by reload_queues().

06749 {
06750    struct call_queue *q = obj;
06751    char *queuename = arg;
06752    if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) {
06753       return CMP_MATCH;
06754    } else {
06755       return 0;
06756    }
06757 }

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

02757 {
02758    struct call_queue *q;
02759    struct queue_ent *current, *prev = NULL;
02760    struct penalty_rule *pr_iter;
02761    int pos = 0;
02762 
02763    if (!(q = qe->parent))
02764       return;
02765    queue_t_ref(q, "Copy queue pointer from queue entry");
02766    ao2_lock(q);
02767 
02768    prev = NULL;
02769    for (current = q->head; current; current = current->next) {
02770       if (current == qe) {
02771          char posstr[20];
02772          q->count--;
02773 
02774          /* Take us out of the queue */
02775          ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave",
02776             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n",
02777             qe->chan->name, q->name,  q->count, qe->pos, qe->chan->uniqueid);
02778          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02779          /* Take us out of the queue */
02780          if (prev)
02781             prev->next = current->next;
02782          else
02783             q->head = current->next;
02784          /* Free penalty rules */
02785          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02786             ast_free(pr_iter);
02787          snprintf(posstr, sizeof(posstr), "%d", qe->pos);
02788          pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr);
02789       } else {
02790          /* Renumber the people after us in the queue based on a new count */
02791          current->pos = ++pos;
02792          prev = current;
02793       }
02794    }
02795    ao2_unlock(q);
02796 
02797    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02798    if (q->realtime) {
02799       struct ast_variable *var;
02800       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02801          q->dead = 1;
02802       } else {
02803          ast_variables_destroy(var);
02804       }
02805    }
02806 
02807    if (q->dead) { 
02808       /* It's dead and nobody is in it, so kill it */
02809       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02810    }
02811    /* unref the explicit ref earlier in the function */
02812    queue_t_unref(q, "Expire copied reference");
02813 }

static int load_module ( void   )  [static]

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

08299 {
08300    int res;
08301    struct ast_context *con;
08302    struct ast_flags mask = {AST_FLAGS_ALL, };
08303 
08304    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
08305 
08306    use_weight = 0;
08307 
08308    if (reload_handler(0, &mask, NULL))
08309       return AST_MODULE_LOAD_DECLINE;
08310 
08311    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
08312    if (!con)
08313       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
08314    else
08315       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
08316 
08317    if (queue_persistent_members)
08318       reload_queue_members();
08319 
08320    ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers));
08321 
08322    ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue));
08323    res = ast_register_application_xml(app, queue_exec);
08324    res |= ast_register_application_xml(app_aqm, aqm_exec);
08325    res |= ast_register_application_xml(app_rqm, rqm_exec);
08326    res |= ast_register_application_xml(app_pqm, pqm_exec);
08327    res |= ast_register_application_xml(app_upqm, upqm_exec);
08328    res |= ast_register_application_xml(app_ql, ql_exec);
08329    res |= ast_manager_register_xml("Queues", 0, manager_queues_show);
08330    res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status);
08331    res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary);
08332    res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member);
08333    res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member);
08334    res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member);
08335    res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom);
08336    res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty);
08337    res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show);
08338    res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload);
08339    res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset);
08340    res |= ast_custom_function_register(&queuevar_function);
08341    res |= ast_custom_function_register(&queueexists_function);
08342    res |= ast_custom_function_register(&queuemembercount_function);
08343    res |= ast_custom_function_register(&queuemembercount_dep);
08344    res |= ast_custom_function_register(&queuememberlist_function);
08345    res |= ast_custom_function_register(&queuewaitingcount_function);
08346    res |= ast_custom_function_register(&queuememberpenalty_function);
08347 
08348    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
08349       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
08350    }
08351 
08352    /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */
08353    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) {
08354       res = -1;
08355    }
08356 
08357    ast_extension_state_add(NULL, NULL, extension_state_cb, NULL);
08358 
08359    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
08360 
08361    return res ? AST_MODULE_LOAD_DECLINE : 0;
08362 }

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

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

02328 {
02329    struct ast_variable *queue_vars;
02330    struct ast_config *member_config = NULL;
02331    struct call_queue *q = NULL, tmpq = {
02332       .name = queuename,   
02333    };
02334    int prev_weight = 0;
02335 
02336    /* Find the queue in the in-core list first. */
02337    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
02338 
02339    if (!q || q->realtime) {
02340       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
02341          queue operations while waiting for the DB.
02342 
02343          This will be two separate database transactions, so we might
02344          see queue parameters as they were before another process
02345          changed the queue and member list as it was after the change.
02346          Thus we might see an empty member list when a queue is
02347          deleted. In practise, this is unlikely to cause a problem. */
02348 
02349       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
02350       if (queue_vars) {
02351          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
02352          if (!member_config) {
02353             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
02354             ast_variables_destroy(queue_vars);
02355             return NULL;
02356          }
02357       }
02358       if (q) {
02359          prev_weight = q->weight ? 1 : 0;
02360       }
02361 
02362       ao2_lock(queues);
02363 
02364       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
02365       if (member_config) {
02366          ast_config_destroy(member_config);
02367       }
02368       if (queue_vars) {
02369          ast_variables_destroy(queue_vars);
02370       }
02371       /* update the use_weight value if the queue's has gained or lost a weight */ 
02372       if (q) {
02373          if (!q->weight && prev_weight) {
02374             ast_atomic_fetchadd_int(&use_weight, -1);
02375          }
02376          if (q->weight && !prev_weight) {
02377             ast_atomic_fetchadd_int(&use_weight, +1);
02378          }
02379       }
02380       /* Other cases will end up with the proper value for use_weight */
02381       ao2_unlock(queues);
02382 
02383    } else {
02384       update_realtime_members(q);
02385    }
02386    return q;
02387 }

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

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

07274 {
07275    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
07276    int paused, penalty = 0;
07277 
07278    queuename = astman_get_header(m, "Queue");
07279    interface = astman_get_header(m, "Interface");
07280    penalty_s = astman_get_header(m, "Penalty");
07281    paused_s = astman_get_header(m, "Paused");
07282    membername = astman_get_header(m, "MemberName");
07283    state_interface = astman_get_header(m, "StateInterface");
07284 
07285    if (ast_strlen_zero(queuename)) {
07286       astman_send_error(s, m, "'Queue' not specified.");
07287       return 0;
07288    }
07289 
07290    if (ast_strlen_zero(interface)) {
07291       astman_send_error(s, m, "'Interface' not specified.");
07292       return 0;
07293    }
07294 
07295    if (ast_strlen_zero(penalty_s))
07296       penalty = 0;
07297    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
07298       penalty = 0;
07299 
07300    if (ast_strlen_zero(paused_s))
07301       paused = 0;
07302    else
07303       paused = abs(ast_true(paused_s));
07304 
07305    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
07306    case RES_OKAY:
07307       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
07308       astman_send_ack(s, m, "Added interface to queue");
07309       break;
07310    case RES_EXISTS:
07311       astman_send_error(s, m, "Unable to add interface: Already there");
07312       break;
07313    case RES_NOSUCHQUEUE:
07314       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
07315       break;
07316    case RES_OUTOFMEMORY:
07317       astman_send_error(s, m, "Out of memory");
07318       break;
07319    }
07320 
07321    return 0;
07322 }

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

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

07359 {
07360    const char *queuename, *interface, *paused_s, *reason;
07361    int paused;
07362 
07363    interface = astman_get_header(m, "Interface");
07364    paused_s = astman_get_header(m, "Paused");
07365    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
07366    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
07367 
07368    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
07369       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
07370       return 0;
07371    }
07372 
07373    paused = abs(ast_true(paused_s));
07374 
07375    if (set_member_paused(queuename, interface, reason, paused))
07376       astman_send_error(s, m, "Interface not found");
07377    else
07378       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
07379    return 0;
07380 }

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

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

07383 {
07384    const char *queuename, *event, *message, *interface, *uniqueid;
07385 
07386    queuename = astman_get_header(m, "Queue");
07387    uniqueid = astman_get_header(m, "UniqueId");
07388    interface = astman_get_header(m, "Interface");
07389    event = astman_get_header(m, "Event");
07390    message = astman_get_header(m, "Message");
07391 
07392    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
07393       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
07394       return 0;
07395    }
07396 
07397    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
07398    astman_send_ack(s, m, "Event added successfully");
07399 
07400    return 0;
07401 }

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

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

07482 {
07483    const char *queuename, *interface, *penalty_s;
07484    int penalty;
07485 
07486    interface = astman_get_header(m, "Interface");
07487    penalty_s = astman_get_header(m, "Penalty");
07488    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
07489    queuename = astman_get_header(m, "Queue");
07490 
07491    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
07492       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
07493       return 0;
07494    }
07495  
07496    penalty = atoi(penalty_s);
07497 
07498    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
07499       astman_send_error(s, m, "Invalid interface, queuename or penalty");
07500    else
07501       astman_send_ack(s, m, "Interface penalty set successfully");
07502 
07503    return 0;
07504 }

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

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

07404 {
07405    struct ast_flags mask = {0,};
07406    const char *queuename = NULL;
07407    int header_found = 0;
07408 
07409    queuename = astman_get_header(m, "Queue");
07410    if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) {
07411       ast_set_flag(&mask, QUEUE_RELOAD_MEMBER);
07412       header_found = 1;
07413    }
07414    if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) {
07415       ast_set_flag(&mask, QUEUE_RELOAD_RULES);
07416       header_found = 1;
07417    }
07418    if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) {
07419       ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS);
07420       header_found = 1;
07421    }
07422 
07423    if (!header_found) {
07424       ast_set_flag(&mask, AST_FLAGS_ALL);
07425    }
07426 
07427    if (!reload_handler(1, &mask, queuename)) {
07428       astman_send_ack(s, m, "Queue reloaded successfully");
07429    } else {
07430       astman_send_error(s, m, "Error encountered while reloading queue");
07431    }
07432    return 0;
07433 }

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

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

07436 {
07437    const char *queuename = NULL;
07438    struct ast_flags mask = {QUEUE_RESET_STATS,};
07439    
07440    queuename = astman_get_header(m, "Queue");
07441 
07442    if (!reload_handler(1, &mask, queuename)) {
07443       astman_send_ack(s, m, "Queue stats reset successfully");
07444    } else {
07445       astman_send_error(s, m, "Error encountered while resetting queue stats");
07446    }
07447    return 0;
07448 }

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

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

07077 {
07078    const char *rule = astman_get_header(m, "Rule");
07079    struct rule_list *rl_iter;
07080    struct penalty_rule *pr_iter;
07081 
07082    AST_LIST_LOCK(&rule_lists);
07083    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
07084       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
07085          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
07086          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
07087             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 );
07088          }
07089          if (!ast_strlen_zero(rule))
07090             break;
07091       }
07092    }
07093    AST_LIST_UNLOCK(&rule_lists);
07094 
07095    astman_append(s, "\r\n\r\n");
07096 
07097    return RESULT_SUCCESS;
07098 }

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

Definition at line 7066 of file app_queue.c.

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

Referenced by load_module().

07067 {
07068    static const char * const a[] = { "queue", "show" };
07069 
07070    __queues_show(s, -1, 2, a);
07071    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
07072 
07073    return RESULT_SUCCESS;
07074 }

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

Queue status info via AMI.

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

07177 {
07178    time_t now;
07179    int pos;
07180    const char *id = astman_get_header(m,"ActionID");
07181    const char *queuefilter = astman_get_header(m,"Queue");
07182    const char *memberfilter = astman_get_header(m,"Member");
07183    char idText[256] = "";
07184    struct call_queue *q;
07185    struct queue_ent *qe;
07186    float sl = 0;
07187    struct member *mem;
07188    struct ao2_iterator queue_iter;
07189    struct ao2_iterator mem_iter;
07190 
07191    astman_send_ack(s, m, "Queue status will follow");
07192    time(&now);
07193    if (!ast_strlen_zero(id))
07194       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
07195 
07196    queue_iter = ao2_iterator_init(queues, 0);
07197    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07198       ao2_lock(q);
07199 
07200       /* List queue properties */
07201       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07202          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
07203          astman_append(s, "Event: QueueParams\r\n"
07204             "Queue: %s\r\n"
07205             "Max: %d\r\n"
07206             "Strategy: %s\r\n"
07207             "Calls: %d\r\n"
07208             "Holdtime: %d\r\n"
07209             "TalkTime: %d\r\n"
07210             "Completed: %d\r\n"
07211             "Abandoned: %d\r\n"
07212             "ServiceLevel: %d\r\n"
07213             "ServicelevelPerf: %2.1f\r\n"
07214             "Weight: %d\r\n"
07215             "%s"
07216             "\r\n",
07217             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted,
07218             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
07219          /* List Queue Members */
07220          mem_iter = ao2_iterator_init(q->members, 0);
07221          while ((mem = ao2_iterator_next(&mem_iter))) {
07222             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
07223                astman_append(s, "Event: QueueMember\r\n"
07224                   "Queue: %s\r\n"
07225                   "Name: %s\r\n"
07226                   "Location: %s\r\n"
07227                   "Membership: %s\r\n"
07228                   "Penalty: %d\r\n"
07229                   "CallsTaken: %d\r\n"
07230                   "LastCall: %d\r\n"
07231                   "Status: %d\r\n"
07232                   "Paused: %d\r\n"
07233                   "%s"
07234                   "\r\n",
07235                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
07236                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
07237             }
07238             ao2_ref(mem, -1);
07239          }
07240          ao2_iterator_destroy(&mem_iter);
07241          /* List Queue Entries */
07242          pos = 1;
07243          for (qe = q->head; qe; qe = qe->next) {
07244             astman_append(s, "Event: QueueEntry\r\n"
07245                "Queue: %s\r\n"
07246                "Position: %d\r\n"
07247                "Channel: %s\r\n"
07248                "Uniqueid: %s\r\n"
07249                "CallerIDNum: %s\r\n"
07250                "CallerIDName: %s\r\n"
07251                "Wait: %ld\r\n"
07252                "%s"
07253                "\r\n",
07254                q->name, pos++, qe->chan->name, qe->chan->uniqueid,
07255                S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),
07256                S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"),
07257                (long) (now - qe->start), idText);
07258          }
07259       }
07260       ao2_unlock(q);
07261       queue_t_unref(q, "Done with iterator");
07262    }
07263    ao2_iterator_destroy(&queue_iter);
07264 
07265    astman_append(s,
07266       "Event: QueueStatusComplete\r\n"
07267       "%s"
07268       "\r\n",idText);
07269 
07270    return RESULT_SUCCESS;
07271 }

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

Summary of queue info via the AMI.

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

07102 {
07103    time_t now;
07104    int qmemcount = 0;
07105    int qmemavail = 0;
07106    int qchancount = 0;
07107    int qlongestholdtime = 0;
07108    const char *id = astman_get_header(m, "ActionID");
07109    const char *queuefilter = astman_get_header(m, "Queue");
07110    char idText[256] = "";
07111    struct call_queue *q;
07112    struct queue_ent *qe;
07113    struct member *mem;
07114    struct ao2_iterator queue_iter;
07115    struct ao2_iterator mem_iter;
07116 
07117    astman_send_ack(s, m, "Queue summary will follow");
07118    time(&now);
07119    if (!ast_strlen_zero(id))
07120       snprintf(idText, 256, "ActionID: %s\r\n", id);
07121    queue_iter = ao2_iterator_init(queues, 0);
07122    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
07123       ao2_lock(q);
07124 
07125       /* List queue properties */
07126       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
07127          /* Reset the necessary local variables if no queuefilter is set*/
07128          qmemcount = 0;
07129          qmemavail = 0;
07130          qchancount = 0;
07131          qlongestholdtime = 0;
07132 
07133          /* List Queue Members */
07134          mem_iter = ao2_iterator_init(q->members, 0);
07135          while ((mem = ao2_iterator_next(&mem_iter))) {
07136             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
07137                ++qmemcount;
07138                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
07139                   ++qmemavail;
07140                }
07141             }
07142             ao2_ref(mem, -1);
07143          }
07144          ao2_iterator_destroy(&mem_iter);
07145          for (qe = q->head; qe; qe = qe->next) {
07146             if ((now - qe->start) > qlongestholdtime) {
07147                qlongestholdtime = now - qe->start;
07148             }
07149             ++qchancount;
07150          }
07151          astman_append(s, "Event: QueueSummary\r\n"
07152             "Queue: %s\r\n"
07153             "LoggedIn: %d\r\n"
07154             "Available: %d\r\n"
07155             "Callers: %d\r\n" 
07156             "HoldTime: %d\r\n"
07157             "TalkTime: %d\r\n"
07158             "LongestHoldTime: %d\r\n"
07159             "%s"
07160             "\r\n",
07161             q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText);
07162       }
07163       ao2_unlock(q);
07164       queue_t_unref(q, "Done with iterator");
07165    }
07166    ao2_iterator_destroy(&queue_iter);
07167    astman_append(s,
07168       "Event: QueueSummaryComplete\r\n"
07169       "%s"
07170       "\r\n", idText);
07171 
07172    return RESULT_SUCCESS;
07173 }

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

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

07325 {
07326    const char *queuename, *interface;
07327 
07328    queuename = astman_get_header(m, "Queue");
07329    interface = astman_get_header(m, "Interface");
07330 
07331    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
07332       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
07333       return 0;
07334    }
07335 
07336    switch (remove_from_queue(queuename, interface)) {
07337    case RES_OKAY:
07338       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
07339       astman_send_ack(s, m, "Removed interface from queue");
07340       break;
07341    case RES_EXISTS:
07342       astman_send_error(s, m, "Unable to remove interface: Not there");
07343       break;
07344    case RES_NOSUCHQUEUE:
07345       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
07346       break;
07347    case RES_OUTOFMEMORY:
07348       astman_send_error(s, m, "Out of memory");
07349       break;
07350    case RES_NOT_DYNAMIC:
07351       astman_send_error(s, m, "Member not dynamic");
07352       break;
07353    }
07354 
07355    return 0;
07356 }

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

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

06738 {
06739    struct call_queue *q = obj;
06740    char *queuename = arg;
06741    if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) {
06742       q->dead = 1;
06743       q->found = 0;
06744    }
06745    return 0;
06746 }

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

Definition at line 6601 of file app_queue.c.

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

Referenced by reload_single_queue().

06602 {
06603    struct member *member = obj;
06604    if (!member->dynamic) {
06605       member->delme = 1;
06606    }
06607    return 0;
06608 }

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

Definition at line 1653 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

01654 {
01655    struct member *mem1 = obj1, *mem2 = obj2;
01656    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
01657 }

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

Definition at line 1641 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

01642 {
01643    const struct member *mem = obj;
01644    const char *chname = strchr(mem->interface, '/');
01645    int ret = 0, i;
01646    if (!chname)
01647       chname = mem->interface;
01648    for (i = 0; i < 5 && chname[i]; i++)
01649       ret += compress_char(chname[i]) << (i * 6);
01650    return ret;
01651 }

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

02862 {
02863    struct member *mem;
02864    int avl = 0;
02865    struct ao2_iterator mem_iter;
02866 
02867    mem_iter = ao2_iterator_init(q->members, 0);
02868    while ((mem = ao2_iterator_next(&mem_iter))) {
02869       switch (mem->status) {
02870       case AST_DEVICE_INUSE:
02871          if (!q->ringinuse)
02872             break;
02873          /* else fall through */
02874       case AST_DEVICE_NOT_INUSE:
02875       case AST_DEVICE_UNKNOWN:
02876          if (!mem->paused) {
02877             avl++;
02878          }
02879          break;
02880       }
02881       ao2_ref(mem, -1);
02882 
02883       /* If autofill is not enabled or if the queue's strategy is ringall, then
02884        * we really don't care about the number of available members so much as we
02885        * do that there is at least one available.
02886        *
02887        * In fact, we purposely will return from this function stating that only
02888        * one member is available if either of those conditions hold. That way,
02889        * functions which determine what action to take based on the number of available
02890        * members will operate properly. The reasoning is that even if multiple
02891        * members are available, only the head caller can actually be serviced.
02892        */
02893       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02894          break;
02895       }
02896    }
02897    ao2_iterator_destroy(&mem_iter);
02898 
02899    return avl;
02900 }

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

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

01834 {
01835    char *value_copy = ast_strdupa(value);
01836    char *option = NULL;
01837    while ((option = strsep(&value_copy, ","))) {
01838       if (!strcasecmp(option, "paused")) {
01839          *empty |= QUEUE_EMPTY_PAUSED;
01840       } else if (!strcasecmp(option, "penalty")) {
01841          *empty |= QUEUE_EMPTY_PENALTY;
01842       } else if (!strcasecmp(option, "inuse")) {
01843          *empty |= QUEUE_EMPTY_INUSE;
01844       } else if (!strcasecmp(option, "ringing")) {
01845          *empty |= QUEUE_EMPTY_RINGING;
01846       } else if (!strcasecmp(option, "invalid")) {
01847          *empty |= QUEUE_EMPTY_INVALID;
01848       } else if (!strcasecmp(option, "wrapup")) {
01849          *empty |= QUEUE_EMPTY_WRAPUP;
01850       } else if (!strcasecmp(option, "unavailable")) {
01851          *empty |= QUEUE_EMPTY_UNAVAILABLE;
01852       } else if (!strcasecmp(option, "unknown")) {
01853          *empty |= QUEUE_EMPTY_UNKNOWN;
01854       } else if (!strcasecmp(option, "loose")) {
01855          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID);
01856       } else if (!strcasecmp(option, "strict")) {
01857          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE);
01858       } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) {
01859          *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED);
01860       } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) {
01861          *empty = 0;
01862       } else {
01863          ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty");
01864       }
01865    }
01866 }

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

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

02532 {
02533    int res;
02534 
02535    if (ast_strlen_zero(filename)) {
02536       return 0;
02537    }
02538 
02539    if (!ast_fileexists(filename, NULL, chan->language)) {
02540       return 0;
02541    }
02542 
02543    ast_stopstream(chan);
02544 
02545    res = ast_streamfile(chan, filename, chan->language);
02546    if (!res)
02547       res = ast_waitstream(chan, AST_DIGIT_ANY);
02548 
02549    ast_stopstream(chan);
02550 
02551    return res;
02552 }

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

PauseQueueMember application.

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

05521 {
05522    char *parse;
05523    AST_DECLARE_APP_ARGS(args,
05524       AST_APP_ARG(queuename);
05525       AST_APP_ARG(interface);
05526       AST_APP_ARG(options);
05527       AST_APP_ARG(reason);
05528    );
05529 
05530    if (ast_strlen_zero(data)) {
05531       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
05532       return -1;
05533    }
05534 
05535    parse = ast_strdupa(data);
05536 
05537    AST_STANDARD_APP_ARGS(args, parse);
05538 
05539    if (ast_strlen_zero(args.interface)) {
05540       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05541       return -1;
05542    }
05543 
05544    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
05545       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
05546       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
05547       return 0;
05548    }
05549 
05550    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
05551 
05552    return 0;
05553 }

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

QueueLog application.

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

05713 {
05714    char *parse;
05715 
05716    AST_DECLARE_APP_ARGS(args,
05717       AST_APP_ARG(queuename);
05718       AST_APP_ARG(uniqueid);
05719       AST_APP_ARG(membername);
05720       AST_APP_ARG(event);
05721       AST_APP_ARG(params);
05722    );
05723 
05724    if (ast_strlen_zero(data)) {
05725       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
05726       return -1;
05727    }
05728 
05729    parse = ast_strdupa(data);
05730 
05731    AST_STANDARD_APP_ARGS(args, parse);
05732 
05733    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
05734        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
05735       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
05736       return -1;
05737    }
05738 
05739    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
05740       "%s", args.params ? args.params : "");
05741 
05742    return 0;
05743 }

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

Definition at line 1260 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

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

static int queue_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 5787 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().

05788 {
05789    int res=-1;
05790    int ringing=0;
05791    const char *user_priority;
05792    const char *max_penalty_str;
05793    const char *min_penalty_str;
05794    int prio;
05795    int qcontinue = 0;
05796    int max_penalty, min_penalty;
05797    enum queue_result reason = QUEUE_UNKNOWN;
05798    /* whether to exit Queue application after the timeout hits */
05799    int tries = 0;
05800    int noption = 0;
05801    char *parse;
05802    int makeannouncement = 0;
05803    int position = 0;
05804    AST_DECLARE_APP_ARGS(args,
05805       AST_APP_ARG(queuename);
05806       AST_APP_ARG(options);
05807       AST_APP_ARG(url);
05808       AST_APP_ARG(announceoverride);
05809       AST_APP_ARG(queuetimeoutstr);
05810       AST_APP_ARG(agi);
05811       AST_APP_ARG(macro);
05812       AST_APP_ARG(gosub);
05813       AST_APP_ARG(rule);
05814       AST_APP_ARG(position);
05815    );
05816    /* Our queue entry */
05817    struct queue_ent qe = { 0 };
05818    
05819    if (ast_strlen_zero(data)) {
05820       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n");
05821       return -1;
05822    }
05823    
05824    parse = ast_strdupa(data);
05825    AST_STANDARD_APP_ARGS(args, parse);
05826 
05827    /* Setup our queue entry */
05828    qe.start = time(NULL);
05829 
05830    /* set the expire time based on the supplied timeout; */
05831    if (!ast_strlen_zero(args.queuetimeoutstr))
05832       qe.expire = qe.start + atoi(args.queuetimeoutstr);
05833    else
05834       qe.expire = 0;
05835 
05836    /* Get the priority from the variable ${QUEUE_PRIO} */
05837    ast_channel_lock(chan);
05838    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
05839    if (user_priority) {
05840       if (sscanf(user_priority, "%30d", &prio) == 1) {
05841          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
05842       } else {
05843          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
05844             user_priority, chan->name);
05845          prio = 0;
05846       }
05847    } else {
05848       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
05849       prio = 0;
05850    }
05851 
05852    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
05853 
05854    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
05855       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
05856          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
05857       } else {
05858          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
05859             max_penalty_str, chan->name);
05860          max_penalty = 0;
05861       }
05862    } else {
05863       max_penalty = 0;
05864    }
05865 
05866    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
05867       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
05868          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
05869       } else {
05870          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
05871             min_penalty_str, chan->name);
05872          min_penalty = 0;
05873       }
05874    } else {
05875       min_penalty = 0;
05876    }
05877    ast_channel_unlock(chan);
05878 
05879    if (args.options && (strchr(args.options, 'r')))
05880       ringing = 1;
05881 
05882    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
05883       qe.ring_when_ringing = 1;
05884    }
05885 
05886    if (args.options && (strchr(args.options, 'c')))
05887       qcontinue = 1;
05888 
05889    if (args.position) {
05890       position = atoi(args.position);
05891       if (position < 0) {
05892          ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename);
05893          position = 0;
05894       }
05895    }
05896 
05897    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
05898       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
05899 
05900    qe.chan = chan;
05901    qe.prio = prio;
05902    qe.max_penalty = max_penalty;
05903    qe.min_penalty = min_penalty;
05904    qe.last_pos_said = 0;
05905    qe.last_pos = 0;
05906    qe.last_periodic_announce_time = time(NULL);
05907    qe.last_periodic_announce_sound = 0;
05908    qe.valid_digits = 0;
05909    if (join_queue(args.queuename, &qe, &reason, position)) {
05910       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
05911       set_queue_result(chan, reason);
05912       return 0;
05913    }
05914    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d",
05915       S_OR(args.url, ""),
05916       S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""),
05917       qe.opos);
05918    copy_rules(&qe, args.rule);
05919    qe.pr = AST_LIST_FIRST(&qe.qe_rules);
05920 check_turns:
05921    if (ringing) {
05922       ast_indicate(chan, AST_CONTROL_RINGING);
05923    } else {
05924       ast_moh_start(chan, qe.moh, NULL);
05925    }
05926 
05927    /* This is the wait loop for callers 2 through maxlen */
05928    res = wait_our_turn(&qe, ringing, &reason);
05929    if (res) {
05930       goto stop;
05931    }
05932 
05933    makeannouncement = 0;
05934 
05935    for (;;) {
05936       /* This is the wait loop for the head caller*/
05937       /* To exit, they may get their call answered; */
05938       /* they may dial a digit from the queue context; */
05939       /* or, they may timeout. */
05940 
05941       /* Leave if we have exceeded our queuetimeout */
05942       if (qe.expire && (time(NULL) >= qe.expire)) {
05943          record_abandoned(&qe);
05944          reason = QUEUE_TIMEOUT;
05945          res = 0;
05946          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
05947             qe.pos, qe.opos, (long) time(NULL) - qe.start);
05948          break;
05949       }
05950 
05951       if (makeannouncement) {
05952          /* Make a position announcement, if enabled */
05953          if (qe.parent->announcefrequency)
05954             if ((res = say_position(&qe,ringing)))
05955                goto stop;
05956       }
05957       makeannouncement = 1;
05958 
05959       /* Make a periodic announcement, if enabled */
05960       if (qe.parent->periodicannouncefrequency)
05961          if ((res = say_periodic_announcement(&qe,ringing)))
05962             goto stop;
05963    
05964       /* Leave if we have exceeded our queuetimeout */
05965       if (qe.expire && (time(NULL) >= qe.expire)) {
05966          record_abandoned(&qe);
05967          reason = QUEUE_TIMEOUT;
05968          res = 0;
05969          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05970          break;
05971       }
05972 
05973       /* see if we need to move to the next penalty level for this queue */
05974       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
05975          update_qe_rule(&qe);
05976       }
05977 
05978       /* Try calling all queue members for 'timeout' seconds */
05979       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
05980       if (res) {
05981          goto stop;
05982       }
05983 
05984       if (qe.parent->leavewhenempty) {
05985          int status = 0;
05986          if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) {
05987             record_abandoned(&qe);
05988             reason = QUEUE_LEAVEEMPTY;
05989             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05990             res = 0;
05991             break;
05992          }
05993       }
05994 
05995       /* exit after 'timeout' cycle if 'n' option enabled */
05996       if (noption && tries >= qe.parent->membercount) {
05997          ast_verb(3, "Exiting on time-out cycle\n");
05998          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05999          record_abandoned(&qe);
06000          reason = QUEUE_TIMEOUT;
06001          res = 0;
06002          break;
06003       }
06004 
06005       
06006       /* Leave if we have exceeded our queuetimeout */
06007       if (qe.expire && (time(NULL) >= qe.expire)) {
06008          record_abandoned(&qe);
06009          reason = QUEUE_TIMEOUT;
06010          res = 0;
06011          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
06012          break;
06013       }
06014 
06015       /* If using dynamic realtime members, we should regenerate the member list for this queue */
06016       update_realtime_members(qe.parent);
06017       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
06018       res = wait_a_bit(&qe);
06019       if (res)
06020          goto stop;
06021 
06022       /* Since this is a priority queue and
06023        * it is not sure that we are still at the head
06024        * of the queue, go and check for our turn again.
06025        */
06026       if (!is_our_turn(&qe)) {
06027          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
06028          goto check_turns;
06029       }
06030    }
06031 
06032 stop:
06033    if (res) {
06034       if (res < 0) {
06035          if (!qe.handled) {
06036             record_abandoned(&qe);
06037             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
06038                "%d|%d|%ld", qe.pos, qe.opos,
06039                (long) time(NULL) - qe.start);
06040             res = -1;
06041          } else if (qcontinue) {
06042             reason = QUEUE_CONTINUE;
06043             res = 0;
06044          }
06045       } else if (qe.valid_digits) {
06046          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
06047             "%s|%d", qe.digits, qe.pos);
06048       }
06049    }
06050 
06051    /* Don't allow return code > 0 */
06052    if (res >= 0) {
06053       res = 0; 
06054       if (ringing) {
06055          ast_indicate(chan, -1);
06056       } else {
06057          ast_moh_stop(chan);
06058       }        
06059       ast_stopstream(chan);
06060    }
06061 
06062    set_queue_variables(qe.parent, qe.chan);
06063 
06064    leave_queue(&qe);
06065    if (reason != QUEUE_UNKNOWN)
06066       set_queue_result(chan, reason);
06067 
06068    if (qe.parent) {
06069       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
06070        * This ref must be taken away right before the queue_ent is destroyed.  In this case
06071        * the queue_ent is about to be returned on the stack */
06072       qe.parent = queue_unref(qe.parent);
06073    }
06074 
06075    return res;
06076 }

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

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

06131 {
06132    struct call_queue *q;
06133 
06134    buf[0] = '\0';
06135 
06136    if (ast_strlen_zero(data)) {
06137       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06138       return -1;
06139    }
06140    q = load_realtime_queue(data);
06141    snprintf(buf, len, "%d", q != NULL? 1 : 0);
06142    if (q) {
06143       queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()");
06144    }
06145 
06146    return 0;
06147 }

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

06349 {
06350    int penalty;
06351    AST_DECLARE_APP_ARGS(args,
06352       AST_APP_ARG(queuename);
06353       AST_APP_ARG(interface);
06354    );
06355    /* Make sure the returned value on error is NULL. */
06356    buf[0] = '\0';
06357 
06358    if (ast_strlen_zero(data)) {
06359       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06360       return -1;
06361    }
06362 
06363    AST_STANDARD_APP_ARGS(args, data);
06364 
06365    if (args.argc < 2) {
06366       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06367       return -1;
06368    }
06369 
06370    penalty = get_member_penalty (args.queuename, args.interface);
06371    
06372    if (penalty >= 0) /* remember that buf is already '\0' */
06373       snprintf (buf, len, "%d", penalty);
06374 
06375    return 0;
06376 }

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

06380 {
06381    int penalty;
06382    AST_DECLARE_APP_ARGS(args,
06383       AST_APP_ARG(queuename);
06384       AST_APP_ARG(interface);
06385    );
06386 
06387    if (ast_strlen_zero(data)) {
06388       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06389       return -1;
06390    }
06391 
06392    AST_STANDARD_APP_ARGS(args, data);
06393 
06394    if (args.argc < 2) {
06395       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
06396       return -1;
06397    }
06398 
06399    penalty = atoi(value);
06400 
06401    if (ast_strlen_zero(args.interface)) {
06402       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
06403       return -1;
06404    }
06405 
06406    /* if queuename = NULL then penalty will be set for interface in all the queues. */
06407    if (set_member_penalty(args.queuename, args.interface, penalty)) {
06408       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
06409       return -1;
06410    }
06411 
06412    return 0;
06413 }

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

06155 {
06156    int count = 0;
06157    struct member *m;
06158    struct ao2_iterator mem_iter;
06159    struct call_queue *q;
06160    char *option;
06161 
06162    if (ast_strlen_zero(data)) {
06163       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06164       return -1;
06165    }
06166 
06167    if ((option = strchr(data, ',')))
06168       *option++ = '\0';
06169    else
06170       option = "logged";
06171    if ((q = load_realtime_queue(data))) {
06172       ao2_lock(q);
06173       if (!strcasecmp(option, "logged")) {
06174          mem_iter = ao2_iterator_init(q->members, 0);
06175          while ((m = ao2_iterator_next(&mem_iter))) {
06176             /* Count the agents who are logged in and presently answering calls */
06177             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06178                count++;
06179             }
06180             ao2_ref(m, -1);
06181          }
06182          ao2_iterator_destroy(&mem_iter);
06183       } else if (!strcasecmp(option, "free")) {
06184          mem_iter = ao2_iterator_init(q->members, 0);
06185          while ((m = ao2_iterator_next(&mem_iter))) {
06186             /* Count the agents who are logged in and presently answering calls */
06187             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
06188                count++;
06189             }
06190             ao2_ref(m, -1);
06191          }
06192          ao2_iterator_destroy(&mem_iter);
06193       } else if (!strcasecmp(option, "ready")) {
06194          time_t now;
06195          time(&now);
06196          mem_iter = ao2_iterator_init(q->members, 0);
06197          while ((m = ao2_iterator_next(&mem_iter))) {
06198             /* Count the agents who are logged in, not paused and not wrapping up */
06199             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) &&
06200                   !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) {
06201                count++;
06202             }
06203             ao2_ref(m, -1);
06204          }
06205          ao2_iterator_destroy(&mem_iter);
06206       } else /* must be "count" */
06207          count = q->membercount;
06208       ao2_unlock(q);
06209       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
06210    } else
06211       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06212 
06213    snprintf(buf, len, "%d", count);
06214 
06215    return 0;
06216 }

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

06224 {
06225    int count = 0;
06226    struct member *m;
06227    struct call_queue *q;
06228    struct ao2_iterator mem_iter;
06229    static int depflag = 1;
06230 
06231    if (depflag) {
06232       depflag = 0;
06233       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");
06234    }
06235 
06236    if (ast_strlen_zero(data)) {
06237       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06238       return -1;
06239    }
06240    
06241    if ((q = load_realtime_queue(data))) {
06242       ao2_lock(q);
06243       mem_iter = ao2_iterator_init(q->members, 0);
06244       while ((m = ao2_iterator_next(&mem_iter))) {
06245          /* Count the agents who are logged in and presently answering calls */
06246          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
06247             count++;
06248          }
06249          ao2_ref(m, -1);
06250       }
06251       ao2_iterator_destroy(&mem_iter);
06252       ao2_unlock(q);
06253       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
06254    } else
06255       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06256 
06257    snprintf(buf, len, "%d", count);
06258 
06259    return 0;
06260 }

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

06300 {
06301    struct call_queue *q, tmpq = {
06302       .name = data,  
06303    };
06304    struct member *m;
06305 
06306    /* Ensure an otherwise empty list doesn't return garbage */
06307    buf[0] = '\0';
06308 
06309    if (ast_strlen_zero(data)) {
06310       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
06311       return -1;
06312    }
06313 
06314    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
06315       int buflen = 0, count = 0;
06316       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
06317 
06318       ao2_lock(q);
06319       while ((m = ao2_iterator_next(&mem_iter))) {
06320          /* strcat() is always faster than printf() */
06321          if (count++) {
06322             strncat(buf + buflen, ",", len - buflen - 1);
06323             buflen++;
06324          }
06325          strncat(buf + buflen, m->interface, len - buflen - 1);
06326          buflen += strlen(m->interface);
06327          /* Safeguard against overflow (negative length) */
06328          if (buflen >= len - 2) {
06329             ao2_ref(m, -1);
06330             ast_log(LOG_WARNING, "Truncating list\n");
06331             break;
06332          }
06333          ao2_ref(m, -1);
06334       }
06335       ao2_iterator_destroy(&mem_iter);
06336       ao2_unlock(q);
06337       queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()");
06338    } else
06339       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06340 
06341    /* We should already be terminated, but let's make sure. */
06342    buf[len - 1] = '\0';
06343 
06344    return 0;
06345 }

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

06264 {
06265    int count = 0;
06266    struct call_queue *q, tmpq = {
06267       .name = data,  
06268    };
06269    struct ast_variable *var = NULL;
06270 
06271    buf[0] = '\0';
06272    
06273    if (ast_strlen_zero(data)) {
06274       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
06275       return -1;
06276    }
06277 
06278    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
06279       ao2_lock(q);
06280       count = q->count;
06281       ao2_unlock(q);
06282       queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()");
06283    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
06284       /* if the queue is realtime but was not found in memory, this
06285        * means that the queue had been deleted from memory since it was 
06286        * "dead." This means it has a 0 waiting count
06287        */
06288       count = 0;
06289       ast_variables_destroy(var);
06290    } else
06291       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06292 
06293    snprintf(buf, len, "%d", count);
06294 
06295    return 0;
06296 }

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

06084 {
06085    int res = -1;
06086    struct call_queue *q, tmpq = {
06087       .name = data,  
06088    };
06089 
06090    char interfacevar[256] = "";
06091    float sl = 0;
06092 
06093    if (ast_strlen_zero(data)) {
06094       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
06095       return -1;
06096    }
06097 
06098    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
06099       ao2_lock(q);
06100       if (q->setqueuevar) {
06101          sl = 0;
06102          res = 0;
06103 
06104          if (q->callscompleted > 0) {
06105             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
06106          }
06107 
06108          snprintf(interfacevar, sizeof(interfacevar),
06109             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
06110             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
06111 
06112          pbx_builtin_setvar_multiple(chan, interfacevar);
06113       }
06114 
06115       ao2_unlock(q);
06116       queue_t_unref(q, "Done with QUEUE() function");
06117    } else {
06118       ast_log(LOG_WARNING, "queue %s was not found\n", data);
06119    }
06120 
06121    snprintf(buf, len, "%d", res);
06122 
06123    return 0;
06124 }

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

Definition at line 1253 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

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

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

Definition at line 1278 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

01279 {
01280    ao2_ref(q, 1);
01281    return q;
01282 }

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

References ast_true(), and ast_variable_retrieve().

Referenced by reload_queues().

06506 {
06507    const char *general_val = NULL;
06508    queue_persistent_members = 0;
06509    if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
06510       queue_persistent_members = ast_true(general_val);
06511    autofill_default = 0;
06512    if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
06513       autofill_default = ast_true(general_val);
06514    montype_default = 0;
06515    if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
06516       if (!strcasecmp(general_val, "mixmonitor"))
06517          montype_default = 1;
06518    }
06519    update_cdr = 0;
06520    if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
06521       update_cdr = ast_true(general_val);
06522    shared_lastcall = 0;
06523    if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
06524       shared_lastcall = ast_true(general_val);
06525 }

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

01877 {
01878    if (!strcasecmp(param, "musicclass") || 
01879       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01880       ast_string_field_set(q, moh, val);
01881    } else if (!strcasecmp(param, "announce")) {
01882       ast_string_field_set(q, announce, val);
01883    } else if (!strcasecmp(param, "context")) {
01884       ast_string_field_set(q, context, val);
01885    } else if (!strcasecmp(param, "timeout")) {
01886       q->timeout = atoi(val);
01887       if (q->timeout < 0)
01888          q->timeout = DEFAULT_TIMEOUT;
01889    } else if (!strcasecmp(param, "ringinuse")) {
01890       q->ringinuse = ast_true(val);
01891    } else if (!strcasecmp(param, "setinterfacevar")) {
01892       q->setinterfacevar = ast_true(val);
01893    } else if (!strcasecmp(param, "setqueuevar")) {
01894       q->setqueuevar = ast_true(val);
01895    } else if (!strcasecmp(param, "setqueueentryvar")) {
01896       q->setqueueentryvar = ast_true(val);
01897    } else if (!strcasecmp(param, "monitor-format")) {
01898       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01899    } else if (!strcasecmp(param, "membermacro")) {
01900       ast_string_field_set(q, membermacro, val);
01901    } else if (!strcasecmp(param, "membergosub")) {
01902       ast_string_field_set(q, membergosub, val);
01903    } else if (!strcasecmp(param, "queue-youarenext")) {
01904       ast_string_field_set(q, sound_next, val);
01905    } else if (!strcasecmp(param, "queue-thereare")) {
01906       ast_string_field_set(q, sound_thereare, val);
01907    } else if (!strcasecmp(param, "queue-callswaiting")) {
01908       ast_string_field_set(q, sound_calls, val);
01909    } else if (!strcasecmp(param, "queue-quantity1")) {
01910       ast_string_field_set(q, queue_quantity1, val);
01911    } else if (!strcasecmp(param, "queue-quantity2")) {
01912       ast_string_field_set(q, queue_quantity2, val);
01913    } else if (!strcasecmp(param, "queue-holdtime")) {
01914       ast_string_field_set(q, sound_holdtime, val);
01915    } else if (!strcasecmp(param, "queue-minutes")) {
01916       ast_string_field_set(q, sound_minutes, val);
01917    } else if (!strcasecmp(param, "queue-minute")) {
01918       ast_string_field_set(q, sound_minute, val);
01919    } else if (!strcasecmp(param, "queue-seconds")) {
01920       ast_string_field_set(q, sound_seconds, val);
01921    } else if (!strcasecmp(param, "queue-thankyou")) {
01922       ast_string_field_set(q, sound_thanks, val);
01923    } else if (!strcasecmp(param, "queue-callerannounce")) {
01924       ast_string_field_set(q, sound_callerannounce, val);
01925    } else if (!strcasecmp(param, "queue-reporthold")) {
01926       ast_string_field_set(q, sound_reporthold, val);
01927    } else if (!strcasecmp(param, "announce-frequency")) {
01928       q->announcefrequency = atoi(val);
01929    } else if (!strcasecmp(param, "min-announce-frequency")) {
01930       q->minannouncefrequency = atoi(val);
01931       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01932    } else if (!strcasecmp(param, "announce-round-seconds")) {
01933       q->roundingseconds = atoi(val);
01934       /* Rounding to any other values just doesn't make sense... */
01935       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01936          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01937          if (linenum >= 0) {
01938             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01939                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01940                val, param, q->name, linenum);
01941          } else {
01942             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01943                "using 0 instead for queue '%s'\n", val, param, q->name);
01944          }
01945          q->roundingseconds=0;
01946       }
01947    } else if (!strcasecmp(param, "announce-holdtime")) {
01948       if (!strcasecmp(val, "once"))
01949          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01950       else if (ast_true(val))
01951          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01952       else
01953          q->announceholdtime = 0;
01954    } else if (!strcasecmp(param, "announce-position")) {
01955       if (!strcasecmp(val, "limit"))
01956          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01957       else if (!strcasecmp(val, "more"))
01958          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01959       else if (ast_true(val))
01960          q->announceposition = ANNOUNCEPOSITION_YES;
01961       else
01962          q->announceposition = ANNOUNCEPOSITION_NO;
01963    } else if (!strcasecmp(param, "announce-position-limit")) {
01964       q->announcepositionlimit = atoi(val);
01965    } else if (!strcasecmp(param, "periodic-announce")) {
01966       if (strchr(val, ',')) {
01967          char *s, *buf = ast_strdupa(val);
01968          unsigned int i = 0;
01969 
01970          while ((s = strsep(&buf, ",|"))) {
01971             if (!q->sound_periodicannounce[i])
01972                q->sound_periodicannounce[i] = ast_str_create(16);
01973             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01974             i++;
01975             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01976                break;
01977          }
01978          q->numperiodicannounce = i;
01979       } else {
01980          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01981          q->numperiodicannounce = 1;
01982       }
01983    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01984       q->periodicannouncefrequency = atoi(val);
01985    } else if (!strcasecmp(param, "relative-periodic-announce")) {
01986       q->relativeperiodicannounce = ast_true(val);
01987    } else if (!strcasecmp(param, "random-periodic-announce")) {
01988       q->randomperiodicannounce = ast_true(val);
01989    } else if (!strcasecmp(param, "retry")) {
01990       q->retry = atoi(val);
01991       if (q->retry <= 0)
01992          q->retry = DEFAULT_RETRY;
01993    } else if (!strcasecmp(param, "wrapuptime")) {
01994       q->wrapuptime = atoi(val);
01995    } else if (!strcasecmp(param, "penaltymemberslimit")) {
01996       if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) {
01997          q->penaltymemberslimit = 0;
01998       }
01999    } else if (!strcasecmp(param, "autofill")) {
02000       q->autofill = ast_true(val);
02001    } else if (!strcasecmp(param, "monitor-type")) {
02002       if (!strcasecmp(val, "mixmonitor"))
02003          q->montype = 1;
02004    } else if (!strcasecmp(param, "autopause")) {
02005       q->autopause = autopause2int(val);
02006    } else if (!strcasecmp(param, "maxlen")) {
02007       q->maxlen = atoi(val);
02008       if (q->maxlen < 0)
02009          q->maxlen = 0;
02010    } else if (!strcasecmp(param, "servicelevel")) {
02011       q->servicelevel= atoi(val);
02012    } else if (!strcasecmp(param, "strategy")) {
02013       int strategy;
02014 
02015       /* We are a static queue and already have set this, no need to do it again */
02016       if (failunknown) {
02017          return;
02018       }
02019       strategy = strat2int(val);
02020       if (strategy < 0) {
02021          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
02022             val, q->name);
02023          q->strategy = QUEUE_STRATEGY_RINGALL;
02024       }
02025       if (strategy == q->strategy) {
02026          return;
02027       }
02028       if (strategy == QUEUE_STRATEGY_LINEAR) {
02029          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
02030          return;
02031       }
02032       q->strategy = strategy;
02033    } else if (!strcasecmp(param, "joinempty")) {
02034       parse_empty_options(val, &q->joinempty, 1);
02035    } else if (!strcasecmp(param, "leavewhenempty")) {
02036       parse_empty_options(val, &q->leavewhenempty, 0);
02037    } else if (!strcasecmp(param, "eventmemberstatus")) {
02038       q->maskmemberstatus = !ast_true(val);
02039    } else if (!strcasecmp(param, "eventwhencalled")) {
02040       if (!strcasecmp(val, "vars")) {
02041          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
02042       } else {
02043          q->eventwhencalled = ast_true(val) ? 1 : 0;
02044       }
02045    } else if (!strcasecmp(param, "reportholdtime")) {
02046       q->reportholdtime = ast_true(val);
02047    } else if (!strcasecmp(param, "memberdelay")) {
02048       q->memberdelay = atoi(val);
02049    } else if (!strcasecmp(param, "weight")) {
02050       q->weight = atoi(val);
02051    } else if (!strcasecmp(param, "timeoutrestart")) {
02052       q->timeoutrestart = ast_true(val);
02053    } else if (!strcasecmp(param, "defaultrule")) {
02054       ast_string_field_set(q, defaultrule, val);
02055    } else if (!strcasecmp(param, "timeoutpriority")) {
02056       if (!strcasecmp(val, "conf")) {
02057          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
02058       } else {
02059          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
02060       }
02061    } else if (failunknown) {
02062       if (linenum >= 0) {
02063          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
02064             q->name, param, linenum);
02065       } else {
02066          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
02067       }
02068    }
02069 }

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

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

07048 {
07049    switch ( cmd ) {
07050    case CLI_INIT:
07051       e->command = "queue show";
07052       e->usage =
07053          "Usage: queue show\n"
07054          "       Provides summary information on a specified queue.\n";
07055       return NULL;
07056    case CLI_GENERATE:
07057       return complete_queue_show(a->line, a->word, a->pos, a->n); 
07058    }
07059 
07060    return __queues_show(NULL, a->fd, a->argc, a->argv);
07061 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 4159 of file app_queue.c.

References ast_free.

04160 {
04161    struct queue_transfer_ds *qtds = data;
04162    ast_free(qtds);
04163 }

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

04183 {
04184    struct queue_transfer_ds *qtds = data;
04185    struct queue_ent *qe = qtds->qe;
04186    struct member *member = qtds->member;
04187    time_t callstart = qtds->starttime;
04188    int callcompletedinsl = qtds->callcompletedinsl;
04189    struct ast_datastore *datastore;
04190 
04191    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04192             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
04193             (long) (time(NULL) - callstart), qe->opos);
04194 
04195    update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart));
04196    
04197    /* No need to lock the channels because they are already locked in ast_do_masquerade */
04198    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
04199       ast_channel_datastore_remove(old_chan, datastore);
04200    } else {
04201       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
04202    }
04203 }

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

Definition at line 1284 of file app_queue.c.

References ao2_ref.

Referenced by queues_data_provider_get().

01285 {
01286    ao2_ref(q, -1);
01287    return q;
01288 }

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

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

08195 {
08196    struct ao2_iterator i;
08197    struct call_queue *queue, *queue_realtime = NULL;
08198    struct ast_config *cfg;
08199    char *queuename;
08200 
08201    /* load realtime queues. */
08202    cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
08203    if (cfg) {
08204       for (queuename = ast_category_browse(cfg, NULL);
08205             !ast_strlen_zero(queuename);
08206             queuename = ast_category_browse(cfg, queuename)) {
08207          if ((queue = load_realtime_queue(queuename))) {
08208             queue_unref(queue);
08209          }
08210       }
08211       ast_config_destroy(cfg);
08212    }
08213 
08214    /* static queues. */
08215    i = ao2_iterator_init(queues, 0);
08216    while ((queue = ao2_iterator_next(&i))) {
08217       ao2_lock(queue);
08218       if (queue->realtime && !(queue_realtime = load_realtime_queue(queue->name))) {
08219          ao2_unlock(queue);
08220          queue_unref(queue);
08221          continue;
08222       } else if (queue->realtime) {
08223          queue_unref(queue_realtime);
08224       }
08225 
08226       queues_data_provider_get_helper(search, data_root, queue);
08227       ao2_unlock(queue);
08228       queue_unref(queue);
08229    }
08230 
08231    return 0;
08232 }

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

08091 {
08092    struct ao2_iterator im;
08093    struct member *member;
08094    struct queue_ent *qe;
08095    struct ast_data *data_queue, *data_members = NULL, *enum_node;
08096    struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel;
08097 
08098    data_queue = ast_data_add_node(data_root, "queue");
08099    if (!data_queue) {
08100       return;
08101    }
08102 
08103    ast_data_add_structure(call_queue, data_queue, queue);
08104 
08105    ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy));
08106 
08107    /* announce position */
08108    enum_node = ast_data_add_node(data_queue, "announceposition");
08109    if (!enum_node) {
08110       return;
08111    }
08112    switch (queue->announceposition) {
08113    case ANNOUNCEPOSITION_LIMIT:
08114       ast_data_add_str(enum_node, "text", "limit");
08115       break;
08116    case ANNOUNCEPOSITION_MORE_THAN:
08117       ast_data_add_str(enum_node, "text", "more");
08118       break;
08119    case ANNOUNCEPOSITION_YES:
08120       ast_data_add_str(enum_node, "text", "yes");
08121       break;
08122    case ANNOUNCEPOSITION_NO:
08123       ast_data_add_str(enum_node, "text", "no");
08124       break;
08125    default:
08126       ast_data_add_str(enum_node, "text", "unknown");
08127       break;
08128    }
08129    ast_data_add_int(enum_node, "value", queue->announceposition);
08130 
08131    /* add queue members */
08132    im = ao2_iterator_init(queue->members, 0);
08133    while ((member = ao2_iterator_next(&im))) {
08134       if (!data_members) {
08135          data_members = ast_data_add_node(data_queue, "members");
08136          if (!data_members) {
08137             ao2_ref(member, -1);
08138             continue;
08139          }
08140       }
08141 
08142       data_member = ast_data_add_node(data_members, "member");
08143       if (!data_member) {
08144          ao2_ref(member, -1);
08145          continue;
08146       }
08147 
08148       ast_data_add_structure(member, data_member, member);
08149 
08150       ao2_ref(member, -1);
08151    }
08152 
08153    /* include the callers inside the result. */
08154    if (queue->head) {
08155       for (qe = queue->head; qe; qe = qe->next) {
08156          if (!data_callers) {
08157             data_callers = ast_data_add_node(data_queue, "callers");
08158             if (!data_callers) {
08159                continue;
08160             }
08161          }
08162 
08163          data_caller = ast_data_add_node(data_callers, "caller");
08164          if (!data_caller) {
08165             continue;
08166          }
08167 
08168          ast_data_add_structure(queue_ent, data_caller, qe);
08169 
08170          /* add the caller channel. */
08171          data_caller_channel = ast_data_add_node(data_caller, "channel");
08172          if (!data_caller_channel) {
08173             continue;
08174          }
08175 
08176          ast_channel_data_add_structure(data_caller_channel, qe->chan, 1);
08177       }
08178    }
08179 
08180    /* if this queue doesn't match remove the added queue. */
08181    if (!ast_data_search_match(search, data_queue)) {
08182       ast_data_remove_node(data_root, data_queue);
08183    }
08184 }

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

Definition at line 2737 of file app_queue.c.

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

02738 {
02739    int oldvalue;
02740 
02741    /* Calculate holdtime using an exponential average */
02742    /* Thanks to SRT for this contribution */
02743    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02744 
02745    ao2_lock(qe->parent);
02746    oldvalue = qe->parent->holdtime;
02747    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02748    ao2_unlock(qe->parent);
02749 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

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

03342 {
03343    set_queue_variables(qe->parent, qe->chan);
03344    ao2_lock(qe->parent);
03345    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
03346       "Queue: %s\r\n"
03347       "Uniqueid: %s\r\n"
03348       "Position: %d\r\n"
03349       "OriginalPosition: %d\r\n"
03350       "HoldTime: %d\r\n",
03351       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
03352 
03353    qe->parent->callsabandoned++;
03354    ao2_unlock(qe->parent);
03355 }

static int reload ( void   )  [static]

Definition at line 8364 of file app_queue.c.

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

08365 {
08366    struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,};
08367    ast_unload_realtime("queue_members");
08368    reload_handler(1, &mask, NULL);
08369    return 0;
08370 }

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

06860 {
06861    int res = 0;
06862 
06863    if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) {
06864       res |= reload_queue_rules(reload);
06865    }
06866    if (ast_test_flag(mask, QUEUE_RESET_STATS)) {
06867       res |= clear_stats(queuename);
06868    }
06869    if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) {
06870       res |= reload_queues(reload, mask, queuename);
06871    }
06872    return res;
06873 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

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

05424 {
05425    char *cur_ptr;
05426    const char *queue_name;
05427    char *member;
05428    char *interface;
05429    char *membername = NULL;
05430    char *state_interface;
05431    char *penalty_tok;
05432    int penalty = 0;
05433    char *paused_tok;
05434    int paused = 0;
05435    struct ast_db_entry *db_tree;
05436    struct ast_db_entry *entry;
05437    struct call_queue *cur_queue;
05438    char queue_data[PM_MAX_LEN];
05439 
05440    ao2_lock(queues);
05441 
05442    /* Each key in 'pm_family' is the name of a queue */
05443    db_tree = ast_db_gettree(pm_family, NULL);
05444    for (entry = db_tree; entry; entry = entry->next) {
05445 
05446       queue_name = entry->key + strlen(pm_family) + 2;
05447 
05448       {
05449          struct call_queue tmpq = {
05450             .name = queue_name,
05451          };
05452          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
05453       }  
05454 
05455       if (!cur_queue)
05456          cur_queue = load_realtime_queue(queue_name);
05457 
05458       if (!cur_queue) {
05459          /* If the queue no longer exists, remove it from the
05460           * database */
05461          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
05462          ast_db_del(pm_family, queue_name);
05463          continue;
05464       } 
05465 
05466       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
05467          queue_t_unref(cur_queue, "Expire reload reference");
05468          continue;
05469       }
05470 
05471       cur_ptr = queue_data;
05472       while ((member = strsep(&cur_ptr, ",|"))) {
05473          if (ast_strlen_zero(member))
05474             continue;
05475 
05476          interface = strsep(&member, ";");
05477          penalty_tok = strsep(&member, ";");
05478          paused_tok = strsep(&member, ";");
05479          membername = strsep(&member, ";");
05480          state_interface = strsep(&member, ";");
05481 
05482          if (!penalty_tok) {
05483             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
05484             break;
05485          }
05486          penalty = strtol(penalty_tok, NULL, 10);
05487          if (errno == ERANGE) {
05488             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
05489             break;
05490          }
05491          
05492          if (!paused_tok) {
05493             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
05494             break;
05495          }
05496          paused = strtol(paused_tok, NULL, 10);
05497          if ((errno == ERANGE) || paused < 0 || paused > 1) {
05498             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
05499             break;
05500          }
05501 
05502          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
05503          
05504          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
05505             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
05506             break;
05507          }
05508       }
05509       queue_t_unref(cur_queue, "Expire reload reference");
05510    }
05511 
05512    ao2_unlock(queues);
05513    if (db_tree) {
05514       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
05515       ast_db_freetree(db_tree);
05516    }
05517 }

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

06458 {
06459    struct ast_config *cfg;
06460    struct rule_list *rl_iter, *new_rl;
06461    struct penalty_rule *pr_iter;
06462    char *rulecat = NULL;
06463    struct ast_variable *rulevar = NULL;
06464    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06465    
06466    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
06467       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
06468       return AST_MODULE_LOAD_SUCCESS;
06469    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06470       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
06471       return AST_MODULE_LOAD_SUCCESS;
06472    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06473       ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format.  Aborting.\n");
06474       return AST_MODULE_LOAD_SUCCESS;
06475    }
06476 
06477    AST_LIST_LOCK(&rule_lists);
06478    while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
06479       while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
06480          ast_free(pr_iter);
06481       ast_free(rl_iter);
06482    }
06483    while ((rulecat = ast_category_browse(cfg, rulecat))) {
06484       if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
06485          AST_LIST_UNLOCK(&rule_lists);
06486          return AST_MODULE_LOAD_FAILURE;
06487       } else {
06488          ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
06489          AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
06490          for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
06491             if(!strcasecmp(rulevar->name, "penaltychange"))
06492                insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
06493             else
06494                ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
06495       }
06496    }
06497    AST_LIST_UNLOCK(&rule_lists);
06498 
06499    ast_config_destroy(cfg);
06500 
06501    return AST_MODULE_LOAD_SUCCESS;
06502 }

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

06772 {
06773    struct ast_config *cfg;
06774    char *cat;
06775    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
06776    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06777 
06778    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
06779       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
06780       return -1;
06781    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
06782       return 0;
06783    } else if (cfg == CONFIG_STATUS_FILEINVALID) {
06784       ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format.  Aborting.\n");
06785       return -1;
06786    }
06787 
06788    /* We've made it here, so it looks like we're doing operations on all queues. */
06789    ao2_lock(queues);
06790    
06791    /* Mark all queues as dead for the moment if we're reloading queues.
06792     * For clarity, we could just be reloading members, in which case we don't want to mess
06793     * with the other queue parameters at all*/
06794    if (queue_reload) {
06795       ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename);
06796    }
06797 
06798    /* Chug through config file */
06799    cat = NULL;
06800    while ((cat = ast_category_browse(cfg, cat)) ) {
06801       if (!strcasecmp(cat, "general") && queue_reload) {
06802          queue_set_global_params(cfg);
06803          continue;
06804       }
06805       if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename))
06806          reload_single_queue(cfg, mask, cat);
06807    }
06808 
06809    ast_config_destroy(cfg);
06810    /* Unref all the dead queues if we were reloading queues */
06811    if (queue_reload) {
06812       ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename);
06813    }
06814    ao2_unlock(queues);
06815    return 0;
06816 }

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

06536 {
06537    char *membername, *interface, *state_interface, *tmp;
06538    char *parse;
06539    struct member *cur, *newm;
06540    struct member tmpmem;
06541    int penalty;
06542    AST_DECLARE_APP_ARGS(args,
06543       AST_APP_ARG(interface);
06544       AST_APP_ARG(penalty);
06545       AST_APP_ARG(membername);
06546       AST_APP_ARG(state_interface);
06547    );
06548 
06549    if (ast_strlen_zero(memberdata)) {
06550       ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n");
06551       return;
06552    }
06553 
06554    /* Add a new member */
06555    parse = ast_strdupa(memberdata);
06556             
06557    AST_STANDARD_APP_ARGS(args, parse);
06558 
06559    interface = args.interface;
06560    if (!ast_strlen_zero(args.penalty)) {
06561       tmp = args.penalty;
06562       ast_strip(tmp);
06563       penalty = atoi(tmp);
06564       if (penalty < 0) {
06565          penalty = 0;
06566       }
06567    } else {
06568       penalty = 0;
06569    }
06570 
06571    if (!ast_strlen_zero(args.membername)) {
06572       membername = args.membername;
06573       ast_strip(membername);
06574    } else {
06575       membername = interface;
06576    }
06577 
06578    if (!ast_strlen_zero(args.state_interface)) {
06579       state_interface = args.state_interface;
06580       ast_strip(state_interface);
06581    } else {
06582       state_interface = interface;
06583    }
06584 
06585    /* Find the old position in the list */
06586    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
06587    cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
06588    if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) {
06589       ao2_link(q->members, newm);
06590       ao2_ref(newm, -1);
06591    }
06592    newm = NULL;
06593 
06594    if (cur) {
06595       ao2_ref(cur, -1);
06596    } else {
06597       q->membercount++;
06598    }
06599 }

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

06642 {
06643    int new;
06644    struct call_queue *q = NULL;
06645    /*We're defining a queue*/
06646    struct call_queue tmpq = {
06647       .name = queuename,
06648    };
06649    const char *tmpvar;
06650    const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS);
06651    const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER);
06652    int prev_weight = 0;
06653    struct ast_variable *var;
06654    if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) {
06655       if (queue_reload) {
06656          /* Make one then */
06657          if (!(q = alloc_queue(queuename))) {
06658             return;
06659          }
06660       } else {
06661          /* Since we're not reloading queues, this means that we found a queue
06662           * in the configuration file which we don't know about yet. Just return.
06663           */
06664          return;
06665       }
06666       new = 1;
06667    } else {
06668       new = 0;
06669    }
06670    
06671    if (!new) {
06672       ao2_lock(q);
06673       prev_weight = q->weight ? 1 : 0;
06674    }
06675    /* Check if we already found a queue with this name in the config file */
06676    if (q->found) {
06677       ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename);
06678       if (!new) {
06679          /* It should be impossible to *not* hit this case*/
06680          ao2_unlock(q);
06681       }
06682       queue_t_unref(q, "We exist! Expiring temporary pointer");
06683       return;
06684    }
06685    /* Due to the fact that the "linear" strategy will have a different allocation
06686     * scheme for queue members, we must devise the queue's strategy before other initializations.
06687     * To be specific, the linear strategy needs to function like a linked list, meaning the ao2
06688     * container used will have only a single bucket instead of the typical number.
06689     */
06690    if (queue_reload) {
06691       if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) {
06692          q->strategy = strat2int(tmpvar);
06693          if (q->strategy < 0) {
06694             ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
06695             tmpvar, q->name);
06696             q->strategy = QUEUE_STRATEGY_RINGALL;
06697          }
06698       } else {
06699          q->strategy = QUEUE_STRATEGY_RINGALL;
06700       }
06701       init_queue(q);
06702    }
06703    if (member_reload) {
06704       q->membercount = 0;
06705       ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL);
06706    }
06707    for (var = ast_variable_browse(cfg, queuename); var; var = var->next) {
06708       if (member_reload && !strcasecmp(var->name, "member")) {
06709          reload_single_member(var->value, q);
06710       } else if (queue_reload) {
06711          queue_set_param(q, var->name, var->value, var->lineno, 1);
06712       }
06713    }
06714    /* At this point, we've determined if the queue has a weight, so update use_weight
06715     * as appropriate
06716     */
06717    if (!q->weight && prev_weight) {
06718       ast_atomic_fetchadd_int(&use_weight, -1);
06719    }
06720    else if (q->weight && !prev_weight) {
06721       ast_atomic_fetchadd_int(&use_weight, +1);
06722    }
06723 
06724    /* Free remaining members marked as delme */
06725    if (member_reload) {
06726       ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q);
06727    }
06728 
06729    if (new) {
06730       queues_t_link(queues, q, "Add queue to container");
06731    } else {
06732       ao2_unlock(q);
06733    }
06734    queue_t_unref(q, "Expiring creation reference");
06735 }

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

05159 {
05160    struct call_queue *q, tmpq = {
05161       .name = queuename,   
05162    };
05163    struct member *mem, tmpmem;
05164    int res = RES_NOSUCHQUEUE;
05165 
05166    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05167    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
05168       ao2_lock(queues);
05169       ao2_lock(q);
05170       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
05171          /* XXX future changes should beware of this assumption!! */
05172          if (!mem->dynamic) {
05173             ao2_ref(mem, -1);
05174             ao2_unlock(q);
05175             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
05176             ao2_unlock(queues);
05177             return RES_NOT_DYNAMIC;
05178          }
05179          q->membercount--;
05180          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
05181             "Queue: %s\r\n"
05182             "Location: %s\r\n"
05183             "MemberName: %s\r\n",
05184             q->name, mem->interface, mem->membername);
05185          ao2_unlink(q->members, mem);
05186          ao2_ref(mem, -1);
05187 
05188          if (queue_persistent_members)
05189             dump_queue_members(q);
05190          
05191          res = RES_OKAY;
05192       } else {
05193          res = RES_EXISTS;
05194       }
05195       ao2_unlock(q);
05196       ao2_unlock(queues);
05197       queue_t_unref(q, "Expiring temporary reference");
05198    }
05199 
05200    return res;
05201 }

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

03001 {
03002    int res;
03003    int status;
03004    char tech[256];
03005    char *location;
03006    const char *macrocontext, *macroexten;
03007 
03008    /* on entry here, we know that tmp->chan == NULL */
03009    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
03010       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
03011       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
03012             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
03013       if (qe->chan->cdr)
03014          ast_cdr_busy(qe->chan->cdr);
03015       tmp->stillgoing = 0;
03016       (*busies)++;
03017       return 0;
03018    }
03019 
03020    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
03021       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
03022       if (qe->chan->cdr)
03023          ast_cdr_busy(qe->chan->cdr);
03024       tmp->stillgoing = 0;
03025       return 0;
03026    }
03027 
03028    if (tmp->member->paused) {
03029       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
03030       if (qe->chan->cdr)
03031          ast_cdr_busy(qe->chan->cdr);
03032       tmp->stillgoing = 0;
03033       return 0;
03034    }
03035    if (use_weight && compare_weight(qe->parent,tmp->member)) {
03036       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
03037       if (qe->chan->cdr)
03038          ast_cdr_busy(qe->chan->cdr);
03039       tmp->stillgoing = 0;
03040       (*busies)++;
03041       return 0;
03042    }
03043 
03044    ast_copy_string(tech, tmp->interface, sizeof(tech));
03045    if ((location = strchr(tech, '/')))
03046       *location++ = '\0';
03047    else
03048       location = "";
03049 
03050    /* Request the peer */
03051    tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status);
03052    if (!tmp->chan) {       /* If we can't, just go on to the next call */
03053       if (qe->chan->cdr)
03054          ast_cdr_busy(qe->chan->cdr);
03055       tmp->stillgoing = 0; 
03056 
03057       ao2_lock(qe->parent);
03058       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03059       qe->parent->rrpos++;
03060       qe->linpos++;
03061       ao2_unlock(qe->parent);
03062 
03063       (*busies)++;
03064       return 0;
03065    }
03066 
03067    ast_channel_lock(tmp->chan);
03068    while (ast_channel_trylock(qe->chan)) {
03069       CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan);
03070    }
03071 
03072    if (qe->cancel_answered_elsewhere) {
03073       ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE);
03074    }
03075    tmp->chan->appl = "AppQueue";
03076    tmp->chan->data = "(Outgoing Line)";
03077    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
03078 
03079    /* If the new channel has no callerid, try to guess what it should be */
03080    if (!tmp->chan->caller.id.number.valid) {
03081       if (qe->chan->connected.id.number.valid) {
03082          struct ast_party_caller caller;
03083 
03084          ast_party_caller_set_init(&caller, &tmp->chan->caller);
03085          caller.id = qe->chan->connected.id;
03086          caller.ani = qe->chan->connected.ani;
03087          ast_channel_set_caller_event(tmp->chan, &caller, NULL);
03088       } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) {
03089          ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL);
03090       } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) {
03091          ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 
03092       }
03093       tmp->dial_callerid_absent = 1;
03094    }
03095 
03096    ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting);
03097 
03098    tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select;
03099 
03100    ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller);
03101 
03102    /* Inherit specially named variables from parent channel */
03103    ast_channel_inherit_variables(qe->chan, tmp->chan);
03104    ast_channel_datastore_inherit(qe->chan, tmp->chan);
03105 
03106    /* Presense of ADSI CPE on outgoing channel follows ours */
03107    tmp->chan->adsicpe = qe->chan->adsicpe;
03108 
03109    /* Inherit context and extension */
03110    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
03111    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
03112    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
03113    if (!ast_strlen_zero(macroexten))
03114       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
03115    else
03116       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
03117    if (ast_cdr_isset_unanswered()) {
03118       /* they want to see the unanswered dial attempts! */
03119       /* set up the CDR fields on all the CDRs to give sensical information */
03120       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
03121       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
03122       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
03123       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
03124       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
03125       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
03126       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
03127       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
03128       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
03129       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
03130       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
03131    }
03132 
03133    /* Place the call, but don't wait on the answer */
03134    if ((res = ast_call(tmp->chan, location, 0))) {
03135       /* Again, keep going even if there's an error */
03136       ast_debug(1, "ast call on peer returned %d\n", res);
03137       ast_verb(3, "Couldn't call %s\n", tmp->interface);
03138       ast_channel_unlock(tmp->chan);
03139       ast_channel_unlock(qe->chan);
03140       do_hang(tmp);
03141       (*busies)++;
03142       update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03143       return 0;
03144    } else if (qe->parent->eventwhencalled) {
03145       char vars[2048];
03146 
03147       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
03148          "Queue: %s\r\n"
03149          "AgentCalled: %s\r\n"
03150          "AgentName: %s\r\n"
03151          "ChannelCalling: %s\r\n"
03152          "DestinationChannel: %s\r\n"
03153          "CallerIDNum: %s\r\n"
03154          "CallerIDName: %s\r\n"
03155          "Context: %s\r\n"
03156          "Extension: %s\r\n"
03157          "Priority: %d\r\n"
03158          "Uniqueid: %s\r\n"
03159          "%s",
03160          qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
03161          S_COR(tmp->chan->caller.id.number.valid, tmp->chan->caller.id.number.str, "unknown"),
03162          S_COR(tmp->chan->caller.id.name.valid, tmp->chan->caller.id.name.str, "unknown"),
03163          qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
03164          qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03165       ast_verb(3, "Called %s\n", tmp->interface);
03166    }
03167    ast_channel_unlock(tmp->chan);
03168    ast_channel_unlock(qe->chan);
03169 
03170    update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member));
03171    return 1;
03172 }

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

03201 {
03202    int ret = 0;
03203 
03204    while (ret == 0) {
03205       struct callattempt *best = find_best(outgoing);
03206       if (!best) {
03207          ast_debug(1, "Nobody left to try ringing in queue\n");
03208          break;
03209       }
03210       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03211          struct callattempt *cur;
03212          /* Ring everyone who shares this best metric (for ringall) */
03213          for (cur = outgoing; cur; cur = cur->q_next) {
03214             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
03215                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
03216                ret |= ring_entry(qe, cur, busies);
03217             }
03218          }
03219       } else {
03220          /* Ring just the best channel */
03221          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
03222          ret = ring_entry(qe, best, busies);
03223       }
03224       
03225       /* If we have timed out, break out */
03226       if (qe->expire && (time(NULL) >= qe->expire)) {
03227          ast_debug(1, "Queue timed out while ringing members.\n");
03228          ret = 0;
03229          break;
03230       }
03231    }
03232 
03233    return ret;
03234 }

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

03359 {
03360    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
03361 
03362    /* Stop ringing, and resume MOH if specified */
03363    if (qe->ring_when_ringing) {
03364       ast_indicate(qe->chan, -1);
03365       ast_moh_start(qe->chan, qe->moh, NULL);
03366    }
03367 
03368    if (qe->parent->eventwhencalled) {
03369       char vars[2048];
03370 
03371       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
03372                   "Queue: %s\r\n"
03373                   "Uniqueid: %s\r\n"
03374                   "Channel: %s\r\n"
03375                   "Member: %s\r\n"
03376                   "MemberName: %s\r\n"
03377                   "Ringtime: %d\r\n"
03378                   "%s",
03379                   qe->parent->name,
03380                   qe->chan->uniqueid,
03381                   qe->chan->name,
03382                   interface,
03383                   membername,
03384                   rnatime,
03385                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03386    }
03387    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
03388    if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) {
03389       if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) {
03390          if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
03391             ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n",
03392                interface, qe->parent->name);
03393          } else {
03394             ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
03395          }
03396       } else {
03397          /* If queue autopause is mode all, just don't send any queue to stop.
03398          * the function will stop in all queues */
03399          if (!set_member_paused("", interface, "Auto-Pause", 1)) {
03400             ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n",
03401                   interface, qe->parent->name);
03402          } else {
03403                ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface);
03404          }
03405       }
03406    }
03407    return;
03408 }

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

RemoveQueueMember application.

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

05593 {
05594    int res=-1;
05595    char *parse, *temppos = NULL;
05596    AST_DECLARE_APP_ARGS(args,
05597       AST_APP_ARG(queuename);
05598       AST_APP_ARG(interface);
05599       AST_APP_ARG(options);
05600    );
05601 
05602 
05603    if (ast_strlen_zero(data)) {
05604       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
05605       return -1;
05606    }
05607 
05608    parse = ast_strdupa(data);
05609 
05610    AST_STANDARD_APP_ARGS(args, parse);
05611 
05612    if (ast_strlen_zero(args.interface)) {
05613       args.interface = ast_strdupa(chan->name);
05614       temppos = strrchr(args.interface, '-');
05615       if (temppos)
05616          *temppos = '\0';
05617    }
05618 
05619    ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface);
05620 
05621    switch (remove_from_queue(args.queuename, args.interface)) {
05622    case RES_OKAY:
05623       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
05624       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
05625       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
05626       res = 0;
05627       break;
05628    case RES_EXISTS:
05629       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
05630       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
05631       res = 0;
05632       break;
05633    case RES_NOSUCHQUEUE:
05634       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
05635       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
05636       res = 0;
05637       break;
05638    case RES_NOT_DYNAMIC:
05639       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
05640       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
05641       res = 0;
05642       break;
05643    }
05644 
05645    return res;
05646 }

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

02078 {
02079    struct member *m;
02080    struct ao2_iterator mem_iter;
02081    int penalty = 0;
02082    int paused  = 0;
02083    int found = 0;
02084 
02085    if (ast_strlen_zero(rt_uniqueid)) {
02086       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
02087       return;
02088    }
02089 
02090    if (penalty_str) {
02091       penalty = atoi(penalty_str);
02092       if (penalty < 0)
02093          penalty = 0;
02094    }
02095 
02096    if (paused_str) {
02097       paused = atoi(paused_str);
02098       if (paused < 0)
02099          paused = 0;
02100    }
02101 
02102    /* Find member by realtime uniqueid and update */
02103    mem_iter = ao2_iterator_init(q->members, 0);
02104    while ((m = ao2_iterator_next(&mem_iter))) {
02105       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
02106          m->dead = 0;   /* Do not delete this one. */
02107          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02108          if (paused_str)
02109             m->paused = paused;
02110          if (strcasecmp(state_interface, m->state_interface)) {
02111             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
02112          }     
02113          m->penalty = penalty;
02114          found = 1;
02115          ao2_ref(m, -1);
02116          break;
02117       }
02118       ao2_ref(m, -1);
02119    }
02120    ao2_iterator_destroy(&mem_iter);
02121 
02122    /* Create a new member */
02123    if (!found) {
02124       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
02125          m->dead = 0;
02126          m->realtime = 1;
02127          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
02128          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
02129          ao2_link(q->members, m);
02130          ao2_ref(m, -1);
02131          m = NULL;
02132          q->membercount++;
02133       }
02134    }
02135 }

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

Playback announcement to queued members if peroid has elapsed.

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

03286 {
03287    int res = 0;
03288    time_t now;
03289 
03290    /* Get the current time */
03291    time(&now);
03292 
03293    /* Check to see if it is time to announce */
03294    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
03295       return 0;
03296 
03297    /* Stop the music on hold so we can play our own file */
03298    if (ringing)
03299       ast_indicate(qe->chan,-1);
03300    else
03301       ast_moh_stop(qe->chan);
03302 
03303    ast_verb(3, "Playing periodic announcement\n");
03304    
03305    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
03306       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
03307    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
03308       ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) {
03309       qe->last_periodic_announce_sound = 0;
03310    }
03311    
03312    /* play the announcement */
03313    res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]));
03314 
03315    if (res > 0 && !valid_exit(qe, res))
03316       res = 0;
03317 
03318    /* Resume Music on Hold if the caller is going to stay in the queue */
03319    if (!res) {
03320       if (ringing)
03321          ast_indicate(qe->chan, AST_CONTROL_RINGING);
03322       else
03323          ast_moh_start(qe->chan, qe->moh, NULL);
03324    }
03325 
03326    /* update last_periodic_announce_time */
03327    if (qe->parent->relativeperiodicannounce)
03328       time(&qe->last_periodic_announce_time);
03329    else
03330       qe->last_periodic_announce_time = now;
03331 
03332    /* Update the current periodic announcement to the next announcement */
03333    if (!qe->parent->randomperiodicannounce) {
03334       qe->last_periodic_announce_sound++;
03335    }
03336    
03337    return res;
03338 }

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

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

02594 {
02595    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
02596    int say_thanks = 1;
02597    time_t now;
02598 
02599    /* Let minannouncefrequency seconds pass between the start of each position announcement */
02600    time(&now);
02601    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
02602       return 0;
02603 
02604    /* If either our position has changed, or we are over the freq timer, say position */
02605    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
02606       return 0;
02607 
02608    if (ringing) {
02609       ast_indicate(qe->chan,-1);
02610    } else {
02611       ast_moh_stop(qe->chan);
02612    }
02613 
02614    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
02615       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
02616       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
02617       qe->pos <= qe->parent->announcepositionlimit))
02618          announceposition = 1;
02619 
02620 
02621    if (announceposition == 1) {
02622       /* Say we're next, if we are */
02623       if (qe->pos == 1) {
02624          res = play_file(qe->chan, qe->parent->sound_next);
02625          if (res)
02626             goto playout;
02627          else
02628             goto posout;
02629       } else {
02630          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02631             /* More than Case*/
02632             res = play_file(qe->chan, qe->parent->queue_quantity1);
02633             if (res)
02634                goto playout;
02635             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02636             if (res)
02637                goto playout;
02638          } else {
02639             /* Normal Case */
02640             res = play_file(qe->chan, qe->parent->sound_thereare);
02641             if (res)
02642                goto playout;
02643             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
02644             if (res)
02645                goto playout;
02646          }
02647          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
02648             /* More than Case*/
02649             res = play_file(qe->chan, qe->parent->queue_quantity2);
02650             if (res)
02651                goto playout;
02652          } else {
02653             res = play_file(qe->chan, qe->parent->sound_calls);
02654             if (res)
02655                goto playout;
02656          }
02657       }
02658    }
02659    /* Round hold time to nearest minute */
02660    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
02661 
02662    /* If they have specified a rounding then round the seconds as well */
02663    if (qe->parent->roundingseconds) {
02664       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
02665       avgholdsecs *= qe->parent->roundingseconds;
02666    } else {
02667       avgholdsecs = 0;
02668    }
02669 
02670    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
02671 
02672    /* If the hold time is >1 min, if it's enabled, and if it's not
02673       supposed to be only once and we have already said it, say it */
02674     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
02675         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
02676         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
02677       res = play_file(qe->chan, qe->parent->sound_holdtime);
02678       if (res)
02679          goto playout;
02680 
02681       if (avgholdmins >= 1) {
02682          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
02683          if (res)
02684             goto playout;
02685 
02686          if (avgholdmins == 1) {
02687             res = play_file(qe->chan, qe->parent->sound_minute);
02688             if (res)
02689                goto playout;
02690          } else {
02691             res = play_file(qe->chan, qe->parent->sound_minutes);
02692             if (res)
02693                goto playout;
02694          }
02695       }
02696       if (avgholdsecs >= 1) {
02697          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
02698          if (res)
02699             goto playout;
02700 
02701          res = play_file(qe->chan, qe->parent->sound_seconds);
02702          if (res)
02703             goto playout;
02704       }
02705    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
02706       say_thanks = 0;
02707    }
02708 
02709 posout:
02710    if (qe->parent->announceposition) {
02711       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
02712          qe->chan->name, qe->parent->name, qe->pos);
02713    }
02714    if (say_thanks) {
02715       res = play_file(qe->chan, qe->parent->sound_thanks);
02716    }
02717 playout:
02718 
02719    if ((res > 0 && !valid_exit(qe, res)))
02720       res = 0;
02721 
02722    /* Set our last_pos indicators */
02723    qe->last_pos = now;
02724    qe->last_pos_said = qe->pos;
02725 
02726    /* Don't restart music on hold if we're about to exit the caller from the queue */
02727    if (!res) {
02728       if (ringing) {
02729          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02730       } else {
02731          ast_moh_start(qe->chan, qe->moh, NULL);
02732       }
02733    }
02734    return res;
02735 }

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

04119 {
04120    const char *reason = NULL; /* silence dumb compilers */
04121 
04122    if (!qe->parent->eventwhencalled)
04123       return;
04124 
04125    switch (rsn) {
04126    case CALLER:
04127       reason = "caller";
04128       break;
04129    case AGENT:
04130       reason = "agent";
04131       break;
04132    case TRANSFER:
04133       reason = "transfer";
04134       break;
04135    }
04136 
04137    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
04138       "Queue: %s\r\n"
04139       "Uniqueid: %s\r\n"
04140       "Channel: %s\r\n"
04141       "Member: %s\r\n"
04142       "MemberName: %s\r\n"
04143       "HoldTime: %ld\r\n"
04144       "TalkTime: %ld\r\n"
04145       "Reason: %s\r\n"
04146       "%s",
04147       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04148       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
04149       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
04150 }

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

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

05266 {
05267    int found = 0;
05268    struct call_queue *q;
05269    struct member *mem;
05270    struct ao2_iterator queue_iter;
05271    int failed;
05272 
05273    /* Special event for when all queues are paused - individual events still generated */
05274    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
05275    if (ast_strlen_zero(queuename))
05276       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
05277 
05278    queue_iter = ao2_iterator_init(queues, 0);
05279    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) {
05280       ao2_lock(q);
05281       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05282          if ((mem = interface_exists(q, interface))) {
05283             if (mem->paused == paused) {
05284                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
05285             }
05286 
05287             failed = 0;
05288             if (mem->realtime) {
05289                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
05290             }
05291          
05292             if (failed) {
05293                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
05294                ao2_ref(mem, -1);
05295                ao2_unlock(q);
05296                queue_t_unref(q, "Done with iterator");
05297                continue;
05298             }  
05299             found++;
05300             mem->paused = paused;
05301 
05302             if (queue_persistent_members)
05303                dump_queue_members(q);
05304 
05305             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
05306             
05307             if (!ast_strlen_zero(reason)) {
05308                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05309                   "Queue: %s\r\n"
05310                   "Location: %s\r\n"
05311                   "MemberName: %s\r\n"
05312                   "Paused: %d\r\n"
05313                   "Reason: %s\r\n",
05314                      q->name, mem->interface, mem->membername, paused, reason);
05315             } else {
05316                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
05317                   "Queue: %s\r\n"
05318                   "Location: %s\r\n"
05319                   "MemberName: %s\r\n"
05320                   "Paused: %d\r\n",
05321                      q->name, mem->interface, mem->membername, paused);
05322             }
05323             ao2_ref(mem, -1);
05324          }
05325       }
05326       
05327       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
05328          ao2_unlock(q);
05329          queue_t_unref(q, "Done with iterator");
05330          break;
05331       }
05332       
05333       ao2_unlock(q);
05334       queue_t_unref(q, "Done with iterator");
05335    }
05336    ao2_iterator_destroy(&queue_iter);
05337 
05338    return found ? RESULT_SUCCESS : RESULT_FAILURE;
05339 }

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

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

05343 {
05344    int foundinterface = 0, foundqueue = 0;
05345    struct call_queue *q;
05346    struct member *mem;
05347    struct ao2_iterator queue_iter;
05348 
05349    if (penalty < 0) {
05350       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
05351       return RESULT_FAILURE;
05352    }
05353 
05354    queue_iter = ao2_iterator_init(queues, 0);
05355    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05356       ao2_lock(q);
05357       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
05358          foundqueue++;
05359          if ((mem = interface_exists(q, interface))) {
05360             foundinterface++;
05361             mem->penalty = penalty;
05362             
05363             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
05364             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
05365                "Queue: %s\r\n"
05366                "Location: %s\r\n"
05367                "Penalty: %d\r\n",
05368                q->name, mem->interface, penalty);
05369             ao2_ref(mem, -1);
05370          }
05371       }
05372       ao2_unlock(q);
05373       queue_t_unref(q, "Done with iterator");
05374    }
05375    ao2_iterator_destroy(&queue_iter);
05376 
05377    if (foundinterface) {
05378       return RESULT_SUCCESS;
05379    } else if (!foundqueue) {
05380       ast_log (LOG_ERROR, "Invalid queuename\n"); 
05381    } else {
05382       ast_log (LOG_ERROR, "Invalid interface\n");
05383    }  
05384 
05385    return RESULT_FAILURE;
05386 }

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

sets the QUEUESTATUS channel variable

Definition at line 1197 of file app_queue.c.

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

Referenced by queue_exec().

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

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

Set variables of queue.

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

01293 {
01294    char interfacevar[256]="";
01295    float sl = 0;
01296 
01297    ao2_lock(q);
01298 
01299    if (q->setqueuevar) {
01300       sl = 0;
01301       if (q->callscompleted > 0) 
01302          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
01303 
01304       snprintf(interfacevar, sizeof(interfacevar),
01305          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
01306          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
01307 
01308       ao2_unlock(q);
01309    
01310       pbx_builtin_setvar_multiple(chan, interfacevar); 
01311    } else {
01312       ao2_unlock(q);
01313    }
01314 }

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

04221 {
04222    struct ast_datastore *ds;
04223    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
04224 
04225    if (!qtds) {
04226       ast_log(LOG_WARNING, "Memory allocation error!\n");
04227       return NULL;
04228    }
04229 
04230    ast_channel_lock(qe->chan);
04231    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
04232       ast_channel_unlock(qe->chan);
04233       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
04234       return NULL;
04235    }
04236 
04237    qtds->qe = qe;
04238    /* This member is refcounted in try_calling, so no need to add it here, too */
04239    qtds->member = member;
04240    qtds->starttime = starttime;
04241    qtds->callcompletedinsl = callcompletedinsl;
04242    ds->data = qtds;
04243    ast_channel_datastore_add(qe->chan, ds);
04244    ast_channel_unlock(qe->chan);
04245    return ds;
04246 }

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

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

03262 {
03263    struct callattempt *best = find_best(outgoing);
03264 
03265    if (best) {
03266       /* Ring just the best channel */
03267       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03268       qe->linpos = best->metric % 1000;
03269    } else {
03270       /* Just increment rrpos */
03271       if (qe->linwrapped) {
03272          /* No more channels, start over */
03273          qe->linpos = 0;
03274       } else {
03275          /* Prioritize next entry */
03276          qe->linpos++;
03277       }
03278    }
03279    qe->linwrapped = 0;
03280 
03281    return 0;
03282 }

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

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

03238 {
03239    struct callattempt *best = find_best(outgoing);
03240 
03241    if (best) {
03242       /* Ring just the best channel */
03243       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
03244       qe->parent->rrpos = best->metric % 1000;
03245    } else {
03246       /* Just increment rrpos */
03247       if (qe->parent->wrapped) {
03248          /* No more channels, start over */
03249          qe->parent->rrpos = 0;
03250       } else {
03251          /* Prioritize next entry */
03252          qe->parent->rrpos++;
03253       }
03254    }
03255    qe->parent->wrapped = 0;
03256 
03257    return 0;
03258 }

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

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

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

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

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

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

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

Definition at line 4300 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, QUEUE_STRATEGY_RRORDERED, and call_queue::strategy.

Referenced by queue_exec().

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

static int unload_module ( void   )  [static]

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

08244 {
08245    int res;
08246    struct ast_context *con;
08247    struct ao2_iterator q_iter;
08248    struct call_queue *q = NULL;
08249 
08250    ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue));
08251    res = ast_manager_unregister("QueueStatus");
08252    res |= ast_manager_unregister("Queues");
08253    res |= ast_manager_unregister("QueueRule");
08254    res |= ast_manager_unregister("QueueSummary");
08255    res |= ast_manager_unregister("QueueAdd");
08256    res |= ast_manager_unregister("QueueRemove");
08257    res |= ast_manager_unregister("QueuePause");
08258    res |= ast_manager_unregister("QueueLog");
08259    res |= ast_manager_unregister("QueuePenalty");
08260    res |= ast_unregister_application(app_aqm);
08261    res |= ast_unregister_application(app_rqm);
08262    res |= ast_unregister_application(app_pqm);
08263    res |= ast_unregister_application(app_upqm);
08264    res |= ast_unregister_application(app_ql);
08265    res |= ast_unregister_application(app);
08266    res |= ast_custom_function_unregister(&queueexists_function);
08267    res |= ast_custom_function_unregister(&queuevar_function);
08268    res |= ast_custom_function_unregister(&queuemembercount_function);
08269    res |= ast_custom_function_unregister(&queuemembercount_dep);
08270    res |= ast_custom_function_unregister(&queuememberlist_function);
08271    res |= ast_custom_function_unregister(&queuewaitingcount_function);
08272    res |= ast_custom_function_unregister(&queuememberpenalty_function);
08273 
08274    res |= ast_data_unregister(NULL);
08275 
08276    if (device_state_sub)
08277       ast_event_unsubscribe(device_state_sub);
08278 
08279    ast_extension_state_del(0, extension_state_cb);
08280 
08281    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
08282       ast_context_remove_extension2(con, "s", 1, NULL, 0);
08283       ast_context_destroy(con, "app_queue"); /* leave no trace */
08284    }
08285 
08286    q_iter = ao2_iterator_init(queues, 0);
08287    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
08288       queues_t_unlink(queues, q, "Remove queue from container due to unload");
08289       queue_t_unref(q, "Done with iterator");
08290    }
08291    ao2_iterator_destroy(&q_iter);
08292    ao2_ref(queues, -1);
08293    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
08294    ast_unload_realtime("queue_members");
08295    return res;
08296 }

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

03886 {
03887    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
03888    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
03889    char max_penalty_str[20], min_penalty_str[20]; 
03890    /* a relative change to the penalty could put it below 0 */
03891    if (max_penalty < 0)
03892       max_penalty = 0;
03893    if (min_penalty < 0)
03894       min_penalty = 0;
03895    if (min_penalty > max_penalty)
03896       min_penalty = max_penalty;
03897    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
03898    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
03899    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
03900    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
03901    qe->max_penalty = max_penalty;
03902    qe->min_penalty = min_penalty;
03903    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);
03904    qe->pr = AST_LIST_NEXT(qe->pr, list);
03905 }

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

03994 {
03995    int oldtalktime;
03996 
03997    struct member *mem;
03998    struct call_queue *qtmp;
03999    struct ao2_iterator queue_iter;  
04000    
04001    if (shared_lastcall) {
04002       queue_iter = ao2_iterator_init(queues, 0);
04003       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04004          ao2_lock(qtmp);
04005          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
04006             time(&mem->lastcall);
04007             mem->calls++;
04008             mem->lastqueue = q;
04009             ao2_ref(mem, -1);
04010          }
04011          ao2_unlock(qtmp);
04012          queue_t_unref(qtmp, "Done with iterator");
04013       }
04014       ao2_iterator_destroy(&queue_iter);
04015    } else {
04016       ao2_lock(q);
04017       time(&member->lastcall);
04018       member->calls++;
04019       member->lastqueue = q;
04020       ao2_unlock(q);
04021    }  
04022    ao2_lock(q);
04023    q->callscompleted++;
04024    if (callcompletedinsl)
04025       q->callscompletedinsl++;
04026    /* Calculate talktime using the same exponential average as holdtime code*/
04027    oldtalktime = q->talktime;
04028    q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2;
04029    ao2_unlock(q);
04030    return 0;
04031 }

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

Definition at line 2389 of file app_queue.c.

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

Referenced by set_member_paused().

02390 {
02391    int ret = -1;
02392 
02393    if (ast_strlen_zero(mem->rt_uniqueid))
02394       return ret;
02395 
02396    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
02397       ret = 0;
02398 
02399    return ret;
02400 }

static void update_realtime_members ( struct call_queue q  )  [static]

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

02404 {
02405    struct ast_config *member_config = NULL;
02406    struct member *m;
02407    char *interface = NULL;
02408    struct ao2_iterator mem_iter;
02409 
02410    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
02411       /*This queue doesn't have realtime members*/
02412       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
02413       return;
02414    }
02415 
02416    ao2_lock(queues);
02417    ao2_lock(q);
02418    
02419    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
02420    mem_iter = ao2_iterator_init(q->members, 0);
02421    while ((m = ao2_iterator_next(&mem_iter))) {
02422       if (m->realtime)
02423          m->dead = 1;
02424       ao2_ref(m, -1);
02425    }
02426    ao2_iterator_destroy(&mem_iter);
02427 
02428    while ((interface = ast_category_browse(member_config, interface))) {
02429       rt_handle_member_record(q, interface,
02430          ast_variable_retrieve(member_config, interface, "uniqueid"),
02431          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
02432          ast_variable_retrieve(member_config, interface, "penalty"),
02433          ast_variable_retrieve(member_config, interface, "paused"),
02434          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
02435    }
02436 
02437    /* Delete all realtime members that have been deleted in DB. */
02438    mem_iter = ao2_iterator_init(q->members, 0);
02439    while ((m = ao2_iterator_next(&mem_iter))) {
02440       if (m->dead) {
02441          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
02442          ao2_unlink(q->members, m);
02443          q->membercount--;
02444       }
02445       ao2_ref(m, -1);
02446    }
02447    ao2_iterator_destroy(&mem_iter);
02448    ao2_unlock(q);
02449    ao2_unlock(queues);
02450    ast_config_destroy(member_config);
02451 }

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

References EVENT_FLAG_AGENT, and manager_event.

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

01429 {
01430    m->status = status;
01431 
01432    if (q->maskmemberstatus)
01433       return 0;
01434 
01435    manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
01436       "Queue: %s\r\n"
01437       "Location: %s\r\n"
01438       "MemberName: %s\r\n"
01439       "Membership: %s\r\n"
01440       "Penalty: %d\r\n"
01441       "CallsTaken: %d\r\n"
01442       "LastCall: %d\r\n"
01443       "Status: %d\r\n"
01444       "Paused: %d\r\n",
01445       q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static",
01446       m->penalty, m->calls, (int)m->lastcall, m->status, m->paused
01447    );
01448 
01449    return 0;
01450 }

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

UnPauseQueueMember application.

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

05557 {
05558    char *parse;
05559    AST_DECLARE_APP_ARGS(args,
05560       AST_APP_ARG(queuename);
05561       AST_APP_ARG(interface);
05562       AST_APP_ARG(options);
05563       AST_APP_ARG(reason);
05564    );
05565 
05566    if (ast_strlen_zero(data)) {
05567       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
05568       return -1;
05569    }
05570 
05571    parse = ast_strdupa(data);
05572 
05573    AST_STANDARD_APP_ARGS(args, parse);
05574 
05575    if (ast_strlen_zero(args.interface)) {
05576       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
05577       return -1;
05578    }
05579 
05580    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
05581       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
05582       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
05583       return 0;
05584    }
05585 
05586    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
05587 
05588    return 0;
05589 }

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

02560 {
02561    int digitlen = strlen(qe->digits);
02562 
02563    /* Prevent possible buffer overflow */
02564    if (digitlen < sizeof(qe->digits) - 2) {
02565       qe->digits[digitlen] = digit;
02566       qe->digits[digitlen + 1] = '\0';
02567    } else {
02568       qe->digits[0] = '\0';
02569       return 0;
02570    }
02571 
02572    /* If there's no context to goto, short-circuit */
02573    if (ast_strlen_zero(qe->context))
02574       return 0;
02575 
02576    /* If the extension is bad, then reset the digits to blank */
02577    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1,
02578       S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) {
02579       qe->digits[0] = '\0';
02580       return 0;
02581    }
02582 
02583    /* We have an exact match */
02584    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
02585       qe->valid_digits = 1;
02586       /* Return 1 on a successful goto */
02587       return 1;
02588    }
02589 
02590    return 0;
02591 }

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

02950 {
02951    struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1);
02952    const char *tmp;
02953 
02954    if (pbx_builtin_serialize_variables(chan, &buf)) {
02955       int i, j;
02956 
02957       /* convert "\n" to "\nVariable: " */
02958       strcpy(vars, "Variable: ");
02959       tmp = ast_str_buffer(buf);
02960 
02961       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02962          vars[j] = tmp[i];
02963 
02964          if (tmp[i + 1] == '\0')
02965             break;
02966          if (tmp[i] == '\n') {
02967             vars[j++] = '\r';
02968             vars[j++] = '\n';
02969 
02970             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02971             j += 9;
02972          }
02973       }
02974       if (j > len - 3)
02975          j = len - 3;
02976       vars[j++] = '\r';
02977       vars[j++] = '\n';
02978       vars[j] = '\0';
02979    } else {
02980       /* there are no channel variables; leave it blank */
02981       *vars = '\0';
02982    }
02983    return vars;
02984 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 5073 of file app_queue.c.

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

Referenced by queue_exec().

05074 {
05075    /* Don't need to hold the lock while we setup the outgoing calls */
05076    int retrywait = qe->parent->retry * 1000;
05077 
05078    int res = ast_waitfordigit(qe->chan, retrywait);
05079    if (res > 0 && !valid_exit(qe, res))
05080       res = 0;
05081 
05082    return res;
05083 }

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

03426 {
03427    const char *queue = qe->parent->name;
03428    struct callattempt *o, *start = NULL, *prev = NULL;
03429    int res;
03430    int status;
03431    int numbusies = prebusies;
03432    int numnochan = 0;
03433    int stillgoing = 0;
03434    int orig = *to;
03435    struct ast_frame *f;
03436    struct callattempt *peer = NULL;
03437    struct ast_channel *winner;
03438    struct ast_channel *in = qe->chan;
03439    char on[80] = "";
03440    char membername[80] = "";
03441    long starttime = 0;
03442    long endtime = 0;
03443 #ifdef HAVE_EPOLL
03444    struct callattempt *epollo;
03445 #endif
03446    struct ast_party_connected_line connected_caller;
03447    char *inchan_name;
03448 
03449    ast_party_connected_line_init(&connected_caller);
03450 
03451    ast_channel_lock(qe->chan);
03452    inchan_name = ast_strdupa(qe->chan->name);
03453    ast_channel_unlock(qe->chan);
03454 
03455    starttime = (long) time(NULL);
03456 #ifdef HAVE_EPOLL
03457    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03458       if (epollo->chan)
03459          ast_poll_channel_add(in, epollo->chan);
03460    }
03461 #endif
03462    
03463    while (*to && !peer) {
03464       int numlines, retry, pos = 1;
03465       struct ast_channel *watchers[AST_MAX_WATCHERS];
03466       watchers[0] = in;
03467       start = NULL;
03468 
03469       for (retry = 0; retry < 2; retry++) {
03470          numlines = 0;
03471          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
03472             if (o->stillgoing) { /* Keep track of important channels */
03473                stillgoing = 1;
03474                if (o->chan) {
03475                   watchers[pos++] = o->chan;
03476                   if (!start)
03477                      start = o;
03478                   else
03479                      prev->call_next = o;
03480                   prev = o;
03481                }
03482             }
03483             numlines++;
03484          }
03485          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
03486             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
03487             break;
03488          /* On "ringall" strategy we only move to the next penalty level
03489             when *all* ringing phones are done in the current penalty level */
03490          ring_one(qe, outgoing, &numbusies);
03491          /* and retry... */
03492       }
03493       if (pos == 1 /* not found */) {
03494          if (numlines == (numbusies + numnochan)) {
03495             ast_debug(1, "Everyone is busy at this time\n");
03496          } else {
03497             ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan);
03498          }
03499          *to = 0;
03500          return NULL;
03501       }
03502 
03503       /* Poll for events from both the incoming channel as well as any outgoing channels */
03504       winner = ast_waitfor_n(watchers, pos, to);
03505 
03506       /* Service all of the outgoing channels */
03507       for (o = start; o; o = o->call_next) {
03508          /* We go with a static buffer here instead of using ast_strdupa. Using
03509           * ast_strdupa in a loop like this one can cause a stack overflow
03510           */
03511          char ochan_name[AST_CHANNEL_NAME];
03512          if (o->chan) {
03513             ast_channel_lock(o->chan);
03514             ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name));
03515             ast_channel_unlock(o->chan);
03516          }
03517          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
03518             if (!peer) {
03519                ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03520                if (update_connectedline) {
03521                   if (o->pending_connected_update) {
03522                      if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03523                         ast_channel_update_connected_line(in, &o->connected, NULL);
03524                      }
03525                   } else if (!o->dial_callerid_absent) {
03526                      ast_channel_lock(o->chan);
03527                      ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03528                      ast_channel_unlock(o->chan);
03529                      connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03530                      ast_channel_update_connected_line(in, &connected_caller, NULL);
03531                      ast_party_connected_line_free(&connected_caller);
03532                   }
03533                }
03534                if (o->aoc_s_rate_list) {
03535                   size_t encoded_size;
03536                   struct ast_aoc_encoded *encoded;
03537                   if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03538                      ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03539                      ast_aoc_destroy_encoded(encoded);
03540                   }
03541                }
03542                peer = o;
03543             }
03544          } else if (o->chan && (o->chan == winner)) {
03545 
03546             ast_copy_string(on, o->member->interface, sizeof(on));
03547             ast_copy_string(membername, o->member->membername, sizeof(membername));
03548 
03549             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
03550                ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward);
03551                numnochan++;
03552                do_hang(o);
03553                winner = NULL;
03554                continue;
03555             } else if (!ast_strlen_zero(o->chan->call_forward)) {
03556                struct ast_channel *original = o->chan;
03557                char tmpchan[256];
03558                char *stuff;
03559                char *tech;
03560 
03561                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
03562                if ((stuff = strchr(tmpchan, '/'))) {
03563                   *stuff++ = '\0';
03564                   tech = tmpchan;
03565                } else {
03566                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
03567                   stuff = tmpchan;
03568                   tech = "Local";
03569                }
03570 
03571                ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL);
03572 
03573                /* Before processing channel, go ahead and check for forwarding */
03574                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name);
03575                /* Setup parameters */
03576                o->chan = ast_request(tech, in->nativeformats, in, stuff, &status);
03577                if (!o->chan) {
03578                   ast_log(LOG_NOTICE,
03579                      "Forwarding failed to create channel to dial '%s/%s'\n",
03580                      tech, stuff);
03581                   o->stillgoing = 0;
03582                   numnochan++;
03583                } else {
03584                   struct ast_party_redirecting redirecting;
03585 
03586                   ast_channel_lock(o->chan);
03587                   while (ast_channel_trylock(in)) {
03588                      CHANNEL_DEADLOCK_AVOIDANCE(o->chan);
03589                   }
03590                   ast_channel_inherit_variables(in, o->chan);
03591                   ast_channel_datastore_inherit(in, o->chan);
03592 
03593                   ast_string_field_set(o->chan, accountcode, in->accountcode);
03594 
03595                   ast_channel_set_redirecting(o->chan, &original->redirecting, NULL);
03596                   if (!o->chan->redirecting.from.number.valid
03597                      || ast_strlen_zero(o->chan->redirecting.from.number.str)) {
03598                      /*
03599                       * The call was not previously redirected so it is
03600                       * now redirected from this number.
03601                       */
03602                      ast_party_number_free(&o->chan->redirecting.from.number);
03603                      ast_party_number_init(&o->chan->redirecting.from.number);
03604                      o->chan->redirecting.from.number.valid = 1;
03605                      o->chan->redirecting.from.number.str =
03606                         ast_strdup(S_OR(in->macroexten, in->exten));
03607                   }
03608 
03609                   o->chan->dialed.transit_network_select = in->dialed.transit_network_select;
03610 
03611                   ast_party_caller_copy(&o->chan->caller, &in->caller);
03612                   ast_party_connected_line_copy(&o->chan->connected, &original->connected);
03613 
03614                   /*
03615                    * We must unlock o->chan before calling
03616                    * ast_channel_redirecting_macro, because we put o->chan into
03617                    * autoservice there.  That is pretty much a guaranteed
03618                    * deadlock.  This is why the handling of o->chan's lock may
03619                    * seem a bit unusual here.
03620                    */
03621                   ast_party_redirecting_init(&redirecting);
03622                   ast_party_redirecting_copy(&redirecting, &o->chan->redirecting);
03623                   ast_channel_unlock(o->chan);
03624                   res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0);
03625                   if (res) {
03626                      ast_channel_update_redirecting(in, &redirecting, NULL);
03627                   }
03628                   ast_party_redirecting_free(&redirecting);
03629                   ast_channel_unlock(in);
03630 
03631                   update_connectedline = 1;
03632 
03633                   if (ast_call(o->chan, stuff, 0)) {
03634                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
03635                         tech, stuff);
03636                      do_hang(o);
03637                      numnochan++;
03638                   }
03639                }
03640                /* Hangup the original channel now, in case we needed it */
03641                ast_hangup(winner);
03642                continue;
03643             }
03644             f = ast_read(winner);
03645             if (f) {
03646                if (f->frametype == AST_FRAME_CONTROL) {
03647                   switch (f->subclass.integer) {
03648                   case AST_CONTROL_ANSWER:
03649                      /* This is our guy if someone answered. */
03650                      if (!peer) {
03651                         ast_verb(3, "%s answered %s\n", ochan_name, inchan_name);
03652                         if (update_connectedline) {
03653                            if (o->pending_connected_update) {
03654                               if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) {
03655                                  ast_channel_update_connected_line(in, &o->connected, NULL);
03656                               }
03657                            } else if (!o->dial_callerid_absent) {
03658                               ast_channel_lock(o->chan);
03659                               ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller);
03660                               ast_channel_unlock(o->chan);
03661                               connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER;
03662                               ast_channel_update_connected_line(in, &connected_caller, NULL);
03663                               ast_party_connected_line_free(&connected_caller);
03664                            }
03665                         }
03666                         if (o->aoc_s_rate_list) {
03667                            size_t encoded_size;
03668                            struct ast_aoc_encoded *encoded;
03669                            if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) {
03670                               ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size);
03671                               ast_aoc_destroy_encoded(encoded);
03672                            }
03673                         }
03674                         peer = o;
03675                      }
03676                      break;
03677                   case AST_CONTROL_BUSY:
03678                      ast_verb(3, "%s is busy\n", ochan_name);
03679                      if (in->cdr)
03680                         ast_cdr_busy(in->cdr);
03681                      do_hang(o);
03682                      endtime = (long) time(NULL);
03683                      endtime -= starttime;
03684                      rna(endtime * 1000, qe, on, membername, 0);
03685                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03686                         if (qe->parent->timeoutrestart)
03687                            *to = orig;
03688                         /* Have enough time for a queue member to answer? */
03689                         if (*to > 500) {
03690                            ring_one(qe, outgoing, &numbusies);
03691                            starttime = (long) time(NULL);
03692                         }
03693                      }
03694                      numbusies++;
03695                      break;
03696                   case AST_CONTROL_CONGESTION:
03697                      ast_verb(3, "%s is circuit-busy\n", ochan_name);
03698                      if (in->cdr)
03699                         ast_cdr_busy(in->cdr);
03700                      endtime = (long) time(NULL);
03701                      endtime -= starttime;
03702                      rna(endtime * 1000, qe, on, membername, 0);
03703                      do_hang(o);
03704                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03705                         if (qe->parent->timeoutrestart)
03706                            *to = orig;
03707                         if (*to > 500) {
03708                            ring_one(qe, outgoing, &numbusies);
03709                            starttime = (long) time(NULL);
03710                         }
03711                      }
03712                      numbusies++;
03713                      break;
03714                   case AST_CONTROL_RINGING:
03715                      ast_verb(3, "%s is ringing\n", ochan_name);
03716 
03717                      /* Start ring indication when the channel is ringing, if specified */
03718                      if (qe->ring_when_ringing) {
03719                         ast_moh_stop(qe->chan);
03720                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
03721                      }
03722                      break;
03723                   case AST_CONTROL_OFFHOOK:
03724                      /* Ignore going off hook */
03725                      break;
03726                   case AST_CONTROL_CONNECTED_LINE:
03727                      if (!update_connectedline) {
03728                         ast_verb(3, "Connected line update to %s prevented.\n", inchan_name);
03729                      } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
03730                         struct ast_party_connected_line connected;
03731                         ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name);
03732                         ast_party_connected_line_set_init(&connected, &o->connected);
03733                         ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected);
03734                         ast_party_connected_line_set(&o->connected, &connected, NULL);
03735                         ast_party_connected_line_free(&connected);
03736                         o->pending_connected_update = 1;
03737                      } else {
03738                         if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) {
03739                            ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen);
03740                         }
03741                      }
03742                      break;
03743                   case AST_CONTROL_AOC:
03744                      {
03745                         struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan);
03746                         if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) {
03747                            ast_aoc_destroy_decoded(o->aoc_s_rate_list);
03748                            o->aoc_s_rate_list = decoded;
03749                         } else {
03750                            ast_aoc_destroy_decoded(decoded);
03751                         }
03752                      }
03753                      break;
03754                   case AST_CONTROL_REDIRECTING:
03755                      if (!update_connectedline) {
03756                         ast_verb(3, "Redirecting update to %s prevented\n", inchan_name);
03757                      } else if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03758                         ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name);
03759                         if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) {
03760                            ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen);
03761                         }
03762                      }
03763                      break;
03764                   default:
03765                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer);
03766                      break;
03767                   }
03768                }
03769                ast_frfree(f);
03770             } else { /* ast_read() returned NULL */
03771                endtime = (long) time(NULL) - starttime;
03772                rna(endtime * 1000, qe, on, membername, 1);
03773                do_hang(o);
03774                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
03775                   if (qe->parent->timeoutrestart)
03776                      *to = orig;
03777                   if (*to > 500) {
03778                      ring_one(qe, outgoing, &numbusies);
03779                      starttime = (long) time(NULL);
03780                   }
03781                }
03782             }
03783          }
03784       }
03785 
03786       /* If we received an event from the caller, deal with it. */
03787       if (winner == in) {
03788          f = ast_read(in);
03789          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) {
03790             /* Got hung up */
03791             *to = -1;
03792             if (f) {
03793                if (f->data.uint32) {
03794                   in->hangupcause = f->data.uint32;
03795                }
03796                ast_frfree(f);
03797             }
03798             return NULL;
03799          }
03800          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) {
03801             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer);
03802             *to = 0;
03803             ast_frfree(f);
03804             return NULL;
03805          }
03806          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) {
03807             ast_verb(3, "User pressed digit: %c\n", f->subclass.integer);
03808             *to = 0;
03809             *digit = f->subclass.integer;
03810             ast_frfree(f);
03811             return NULL;
03812          }
03813          ast_frfree(f);
03814       }
03815       if (!*to) {
03816          for (o = start; o; o = o->call_next)
03817             rna(orig, qe, o->interface, o->member->membername, 1);
03818       }
03819    }
03820 
03821 #ifdef HAVE_EPOLL
03822    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
03823       if (epollo->chan)
03824          ast_poll_channel_del(in, epollo->chan);
03825    }
03826 #endif
03827 
03828    return peer;
03829 }

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

03918 {
03919    int res = 0;
03920 
03921    /* This is the holding pen for callers 2 through maxlen */
03922    for (;;) {
03923 
03924       if (is_our_turn(qe))
03925          break;
03926 
03927       /* If we have timed out, break out */
03928       if (qe->expire && (time(NULL) >= qe->expire)) {
03929          *reason = QUEUE_TIMEOUT;
03930          break;
03931       }
03932 
03933       if (qe->parent->leavewhenempty) {
03934          int status = 0;
03935 
03936          if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) {
03937             *reason = QUEUE_LEAVEEMPTY;
03938             ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03939             leave_queue(qe);
03940             break;
03941          }
03942       }
03943 
03944       /* Make a position announcement, if enabled */
03945       if (qe->parent->announcefrequency &&
03946          (res = say_position(qe,ringing)))
03947          break;
03948 
03949       /* If we have timed out, break out */
03950       if (qe->expire && (time(NULL) >= qe->expire)) {
03951          *reason = QUEUE_TIMEOUT;
03952          break;
03953       }
03954 
03955       /* Make a periodic announcement, if enabled */
03956       if (qe->parent->periodicannouncefrequency &&
03957          (res = say_periodic_announcement(qe,ringing)))
03958          break;
03959       
03960       /* see if we need to move to the next penalty level for this queue */
03961       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03962          update_qe_rule(qe);
03963       }
03964 
03965       /* If we have timed out, break out */
03966       if (qe->expire && (time(NULL) >= qe->expire)) {
03967          *reason = QUEUE_TIMEOUT;
03968          break;
03969       }
03970       
03971       /* Wait a second before checking again */
03972       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03973          if (res > 0 && !valid_exit(qe, res))
03974             res = 0;
03975          else
03976             break;
03977       }
03978       
03979       /* If we have timed out, break out */
03980       if (qe->expire && (time(NULL) >= qe->expire)) {
03981          *reason = QUEUE_TIMEOUT;
03982          break;
03983       }
03984    }
03985 
03986    return res;
03987 }


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

char* app = "Queue" [static]

Definition at line 899 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 901 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 905 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 909 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 903 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 907 of file app_queue.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 8378 of file app_queue.c.

int autofill_default = 1 [static]

queues.conf [general] option

Definition at line 923 of file app_queue.c.

struct autopause autopausesmodes[] [static]

Referenced by autopause2int().

struct ast_cli_entry cli_queue[] [static]

Definition at line 7966 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 932 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 883 of file app_queue.c.

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

enum queue_result id

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

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

Persistent Members astdb family.

Definition at line 912 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 7957 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 7963 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 8239 of file app_queue.c.

Referenced by load_module().

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 917 of file app_queue.c.

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

Referenced by set_queue_result().

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 4167 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 6415 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 6430 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 6425 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 6440 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 6445 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

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

struct ast_custom_function queuevar_function [static]

Initial value:

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

Definition at line 6420 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 6435 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 7960 of file app_queue.c.

int shared_lastcall = 1 [static]

queues.conf [general] option

Definition at line 929 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* text

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

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 920 of file app_queue.c.


Generated on Mon Jun 27 16:50:59 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7