Wed Aug 18 22:34:00 2010

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"

Go to the source code of this file.

Data Structures

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  interfaces
struct  member
struct  member_interface
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 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_EMPTY_LOOSE   3
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_EVENT_VARIABLES   3
#define queue_t_ref(a, b)   queue_ref(a)
#define queue_t_unref(a, b)   queue_unref(a)
#define queues_t_link(c, q, tag)   ao2_t_link(c,q,tag)
#define queues_t_unlink(c, q, tag)   ao2_t_unlink(c,q,tag)
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM,
  QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM
}
enum  agent_complete_reason { CALLER, AGENT, TRANSFER }
enum  queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL }
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, char **argv)
 Show queue(s) status and statistics.
static void __reg_module (void)
static void __unreg_module (void)
static int add_to_interfaces (const char *interface)
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, void *data)
 AddQueueMember application.
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
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 clear_and_free_interfaces (void)
static void clear_queue (struct call_queue *q)
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 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 enum queue_member_status get_member_status (struct call_queue *q, int max_penalty, int min_penalty)
 Check if members are available.
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_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_reload (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)
 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 interface_exists_global (const char *interface, int lock_queue_container)
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, const char *overriding_rule)
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_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 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 int play_file (struct ast_channel *chan, const char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
 PauseQueueMember application.
static int ql_exec (struct ast_channel *chan, void *data)
 QueueLog application.
static int queue_cmp_cb (void *obj, void *arg, int flags)
static int queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
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 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_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 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 void reload_queue_members (void)
 Reload dynamic queue members persisted into the astdb.
static int reload_queue_rules (int reload)
static int reload_queues (int reload)
static int remove_from_interfaces (const char *interface, int lock_queue_container)
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, void *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 (char *queuename, 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)
 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 (const char *interface, 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, void *data)
 UnPauseQueueMember application.
static int valid_exit (struct queue_ent *qe, char digit)
 Check for valid exit from queue via goto.
static char * vars2manager (struct ast_channel *chan, char *vars, size_t len)
 convert "\n" to "\nVariable: " ready for manager to use
static int wait_a_bit (struct queue_ent *qe)
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 Wait for a member to answer the call.
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)
 The waiting areas for callers who are not actively calling members.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static struct ast_module_infoast_module_info = &__mod_info
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_queue []
static char * descrip
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 * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static int queue_keep_stats = 0
 queues.conf [general] option
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 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_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 0
 queues.conf [general] option
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
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 420 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

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

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_MORE_THAN   3

We say "Currently there are more than <limit>"

Definition at line 435 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 434 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 433 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 140 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 146 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 282 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_LOOSE   3

Definition at line 419 of file app_queue.c.

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

#define QUEUE_EMPTY_NORMAL   1

Definition at line 417 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 418 of file app_queue.c.

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

#define QUEUE_EVENT_VARIABLES   3

Definition at line 422 of file app_queue.c.

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

#define queue_t_ref ( a,
 )     queue_ref(a)

Definition at line 613 of file app_queue.c.

Referenced by leave_queue().

#define queue_t_unref ( a,
 )     queue_unref(a)

Definition at line 614 of file app_queue.c.

Referenced by __queues_show(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), find_queue_by_name_rt(), get_member_penalty(), interface_exists_global(), leave_queue(), manager_queues_status(), manager_queues_summary(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_queues(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_status().

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

Definition at line 615 of file app_queue.c.

Referenced by reload_queues().

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

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

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 149 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 151 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 152 of file app_queue.c.

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

#define RES_OKAY   0

Action completed

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

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


Enumeration Type Documentation

anonymous enum

Please read before modifying this file.
There are three locks which are regularly used throughout this file, the queue list lock, the lock for each individual queue, and the interface list lock. Please be extra careful to always lock in the following order 1) queue list lock 2) individual queue lock 3) interface list lock This order has sort of "evolved" over the lifetime of this application, but it is now in place this way, so please adhere to this order!
Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 
QUEUE_STRATEGY_LINEAR 
QUEUE_STRATEGY_WRANDOM 

Definition at line 114 of file app_queue.c.

enum agent_complete_reason

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3185 of file app_queue.c.

03185                            {
03186    CALLER,
03187    AGENT,
03188    TRANSFER
03189 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 674 of file app_queue.c.

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 308 of file app_queue.c.

00308                   {
00309    QUEUE_UNKNOWN = 0,
00310    QUEUE_TIMEOUT = 1,
00311    QUEUE_JOINEMPTY = 2,
00312    QUEUE_LEAVEEMPTY = 3,
00313    QUEUE_JOINUNAVAIL = 4,
00314    QUEUE_LEAVEUNAVAIL = 5,
00315    QUEUE_FULL = 6,
00316    QUEUE_CONTINUE = 7,
00317 };

enum queue_timeout_priority

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 333 of file app_queue.c.

00333                             {
00334    TIMEOUT_PRIORITY_APP,
00335    TIMEOUT_PRIORITY_CONF,
00336 };


Function Documentation

static char* __queues_show ( struct mansession s,
int  fd,
int  argc,
char **  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 5798 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_load_realtime_multientry(), ast_str_alloca, ast_str_append(), 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, devstate2str(), 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, queues, member::realtime, call_queue::realtime, s, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, ast_str::str, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

05799 {
05800    struct call_queue *q;
05801    struct ast_str *out = ast_str_alloca(240);
05802    int found = 0;
05803    time_t now = time(NULL);
05804    struct ao2_iterator queue_iter;
05805    struct ao2_iterator mem_iter;
05806 
05807    if (argc != 2 && argc != 3)
05808       return CLI_SHOWUSAGE;
05809 
05810    if (argc == 3) { /* specific queue */
05811       if ((q = load_realtime_queue(argv[2]))) {
05812          queue_t_unref(q, "Expiring queue loaded from realtime");
05813       }
05814    } else if (ast_check_realtime("queues")) {
05815       /* This block is to find any queues which are defined in realtime but
05816        * which have not yet been added to the in-core container
05817        */
05818       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05819       char *queuename;
05820       if (cfg) {
05821          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05822             if ((q = load_realtime_queue(queuename))) {
05823                queue_t_unref(q, "Expiring queue loaded from realtime");
05824             }
05825          }
05826          ast_config_destroy(cfg);
05827       }
05828    }
05829 
05830    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
05831    ao2_lock(queues);
05832    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05833       float sl;
05834       struct call_queue *realtime_queue = NULL;
05835 
05836       ao2_lock(q);
05837       /* This check is to make sure we don't print information for realtime
05838        * queues which have been deleted from realtime but which have not yet
05839        * been deleted from the in-core container
05840        */
05841       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05842          ao2_unlock(q);
05843          queue_t_unref(q, "Done with iterator");
05844          continue;
05845       } else if (q->realtime) {
05846          queue_t_unref(realtime_queue, "Expire queue loaded from realtime");
05847       }
05848       if (argc == 3 && strcasecmp(q->name, argv[2])) {
05849          ao2_unlock(q);
05850          queue_t_unref(q, "Done with iterator");
05851          continue;
05852       }
05853       found = 1;
05854 
05855       ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count);
05856       if (q->maxlen)
05857          ast_str_append(&out, 0, "%d", q->maxlen);
05858       else
05859          ast_str_append(&out, 0, "unlimited");
05860       sl = 0;
05861       if (q->callscompleted > 0)
05862          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05863       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05864          int2strat(q->strategy), q->holdtime, q->weight,
05865          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05866       do_print(s, fd, out->str);
05867       if (!ao2_container_count(q->members))
05868          do_print(s, fd, "   No Members");
05869       else {
05870          struct member *mem;
05871 
05872          do_print(s, fd, "   Members: ");
05873          mem_iter = ao2_iterator_init(q->members, 0);
05874          while ((mem = ao2_iterator_next(&mem_iter))) {
05875             ast_str_set(&out, 0, "      %s", mem->membername);
05876             if (strcasecmp(mem->membername, mem->interface)) {
05877                ast_str_append(&out, 0, " (%s)", mem->interface);
05878             }
05879             if (mem->penalty)
05880                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05881             ast_str_append(&out, 0, "%s%s%s (%s)",
05882                mem->dynamic ? " (dynamic)" : "",
05883                mem->realtime ? " (realtime)" : "",
05884                mem->paused ? " (paused)" : "",
05885                devstate2str(mem->status));
05886             if (mem->calls)
05887                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05888                   mem->calls, (long) (time(NULL) - mem->lastcall));
05889             else
05890                ast_str_append(&out, 0, " has taken no calls yet");
05891             do_print(s, fd, out->str);
05892             ao2_ref(mem, -1);
05893          }
05894          ao2_iterator_destroy(&mem_iter);
05895       }
05896       if (!q->head)
05897          do_print(s, fd, "   No Callers");
05898       else {
05899          struct queue_ent *qe;
05900          int pos = 1;
05901 
05902          do_print(s, fd, "   Callers: ");
05903          for (qe = q->head; qe; qe = qe->next) {
05904             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
05905                pos++, qe->chan->name, (long) (now - qe->start) / 60,
05906                (long) (now - qe->start) % 60, qe->prio);
05907             do_print(s, fd, out->str);
05908          }
05909       }
05910       do_print(s, fd, ""); /* blank line between entries */
05911       ao2_unlock(q);
05912       queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */
05913    }
05914    ao2_iterator_destroy(&queue_iter);
05915    ao2_unlock(queues);
05916    if (!found) {
05917       if (argc == 3)
05918          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05919       else
05920          ast_str_set(&out, 0, "No queues.");
05921       do_print(s, fd, out->str);
05922    }
05923    return CLI_SUCCESS;
05924 }

static void __reg_module ( void   )  [static]

Definition at line 6868 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 6868 of file app_queue.c.

static int add_to_interfaces ( const char *  interface  )  [static]

Definition at line 983 of file app_queue.c.

References ast_calloc, ast_copy_string(), ast_debug, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member_interface::interface, and member_interface::list.

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

00984 {
00985    struct member_interface *curint;
00986 
00987    AST_LIST_LOCK(&interfaces);
00988    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00989       if (!strcasecmp(curint->interface, interface))
00990          break;
00991    }
00992 
00993    if (curint) {
00994       AST_LIST_UNLOCK(&interfaces);
00995       return 0;
00996    }
00997 
00998    ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00999    
01000    if ((curint = ast_calloc(1, sizeof(*curint)))) {
01001       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
01002       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
01003    }
01004    AST_LIST_UNLOCK(&interfaces);
01005 
01006    return 0;
01007 }

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

References add_to_interfaces(), 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, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, member::state_interface, and member::status.

Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().

04256 {
04257    struct call_queue *q;
04258    struct member *new_member, *old_member;
04259    int res = RES_NOSUCHQUEUE;
04260 
04261    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
04262     * short-circuits if the queue is already in memory. */
04263    if (!(q = load_realtime_queue(queuename)))
04264       return res;
04265 
04266    ao2_lock(queues);
04267 
04268    ao2_lock(q);
04269    if ((old_member = interface_exists(q, interface)) == NULL) {
04270       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04271          add_to_interfaces(new_member->state_interface);
04272          new_member->dynamic = 1;
04273          ao2_link(q->members, new_member);
04274          q->membercount++;
04275          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04276             "Queue: %s\r\n"
04277             "Location: %s\r\n"
04278             "MemberName: %s\r\n"
04279             "Membership: %s\r\n"
04280             "Penalty: %d\r\n"
04281             "CallsTaken: %d\r\n"
04282             "LastCall: %d\r\n"
04283             "Status: %d\r\n"
04284             "Paused: %d\r\n",
04285             q->name, new_member->interface, new_member->membername,
04286             "dynamic",
04287             new_member->penalty, new_member->calls, (int) new_member->lastcall,
04288             new_member->status, new_member->paused);
04289          
04290          ao2_ref(new_member, -1);
04291          new_member = NULL;
04292 
04293          if (dump)
04294             dump_queue_members(q);
04295          
04296          res = RES_OKAY;
04297       } else {
04298          res = RES_OUTOFMEMORY;
04299       }
04300    } else {
04301       ao2_ref(old_member, -1);
04302       res = RES_EXISTS;
04303    }
04304    ao2_unlock(q);
04305    ao2_unlock(queues);
04306 
04307    return res;
04308 }

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

Definition at line 1466 of file app_queue.c.

References ao2_t_alloc, ao2_t_ref, ast_string_field_init, ast_string_field_set, and destroy_queue().

Referenced by find_queue_by_name_rt(), and reload_queues().

01467 {
01468    struct call_queue *q;
01469 
01470    if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) {
01471       if (ast_string_field_init(q, 64)) {
01472          ao2_t_ref(q, -1, "String field allocation failed");
01473          return NULL;
01474       }
01475       ast_string_field_set(q, name, queuename);
01476    }
01477    return q;
01478 }

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

AddQueueMember application.

Definition at line 4692 of file app_queue.c.

References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, 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().

04693 {
04694    int res=-1;
04695    char *parse, *temppos = NULL;
04696    AST_DECLARE_APP_ARGS(args,
04697       AST_APP_ARG(queuename);
04698       AST_APP_ARG(interface);
04699       AST_APP_ARG(penalty);
04700       AST_APP_ARG(options);
04701       AST_APP_ARG(membername);
04702       AST_APP_ARG(state_interface);
04703    );
04704    int penalty = 0;
04705 
04706    if (ast_strlen_zero(data)) {
04707       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04708       return -1;
04709    }
04710 
04711    parse = ast_strdupa(data);
04712 
04713    AST_STANDARD_APP_ARGS(args, parse);
04714 
04715    if (ast_strlen_zero(args.interface)) {
04716       args.interface = ast_strdupa(chan->name);
04717       temppos = strrchr(args.interface, '-');
04718       if (temppos)
04719          *temppos = '\0';
04720    }
04721 
04722    if (!ast_strlen_zero(args.penalty)) {
04723       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04724          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04725          penalty = 0;
04726       }
04727    }
04728 
04729    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04730    case RES_OKAY:
04731       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04732       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04733       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04734       res = 0;
04735       break;
04736    case RES_EXISTS:
04737       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04738       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04739       res = 0;
04740       break;
04741    case RES_NOSUCHQUEUE:
04742       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04743       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04744       res = 0;
04745       break;
04746    case RES_OUTOFMEMORY:
04747       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04748       break;
04749    }
04750 
04751    return res;
04752 }

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

References ast_channel_datastore_find(), chan, and queue_transfer_info.

03290 {
03291    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03292 }

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

References ast_log(), ast_random(), member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, queue_ent::min_penalty, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_RRMEMORY, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

03129 {
03130    if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03131       return -1;
03132 
03133    switch (q->strategy) {
03134    case QUEUE_STRATEGY_RINGALL:
03135       /* Everyone equal, except for penalty */
03136       tmp->metric = mem->penalty * 1000000;
03137       break;
03138    case QUEUE_STRATEGY_LINEAR:
03139       if (pos < qe->linpos) {
03140          tmp->metric = 1000 + pos;
03141       } else {
03142          if (pos > qe->linpos)
03143             /* Indicate there is another priority */
03144             qe->linwrapped = 1;
03145          tmp->metric = pos;
03146       }
03147       tmp->metric += mem->penalty * 1000000;
03148       break;
03149    case QUEUE_STRATEGY_RRMEMORY:
03150       if (pos < q->rrpos) {
03151          tmp->metric = 1000 + pos;
03152       } else {
03153          if (pos > q->rrpos)
03154             /* Indicate there is another priority */
03155             q->wrapped = 1;
03156          tmp->metric = pos;
03157       }
03158       tmp->metric += mem->penalty * 1000000;
03159       break;
03160    case QUEUE_STRATEGY_RANDOM:
03161       tmp->metric = ast_random() % 1000;
03162       tmp->metric += mem->penalty * 1000000;
03163       break;
03164    case QUEUE_STRATEGY_WRANDOM:
03165       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03166       break;
03167    case QUEUE_STRATEGY_FEWESTCALLS:
03168       tmp->metric = mem->calls;
03169       tmp->metric += mem->penalty * 1000000;
03170       break;
03171    case QUEUE_STRATEGY_LEASTRECENT:
03172       if (!mem->lastcall)
03173          tmp->metric = 0;
03174       else
03175          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03176       tmp->metric += mem->penalty * 1000000;
03177       break;
03178    default:
03179       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03180       break;
03181    }
03182    return 0;
03183 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1059 of file app_queue.c.

References ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and member_interface::list.

Referenced by unload_module().

01060 {
01061    struct member_interface *curint;
01062 
01063    AST_LIST_LOCK(&interfaces);
01064    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01065       ast_free(curint);
01066    AST_LIST_UNLOCK(&interfaces);
01067 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 974 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00975 {
00976    q->holdtime = 0;
00977    q->callscompleted = 0;
00978    q->callsabandoned = 0;
00979    q->callscompletedinsl = 0;
00980    q->wrapuptime = 0;
00981 }

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

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

02159 {
02160    struct call_queue *q;
02161    struct member *mem;
02162    int found = 0;
02163    struct ao2_iterator queue_iter;
02164    
02165    /* q's lock and rq's lock already set by try_calling()
02166     * to solve deadlock */
02167    queue_iter = ao2_iterator_init(queues, 0);
02168    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
02169       if (q == rq) { /* don't check myself, could deadlock */
02170          queue_t_unref(q, "Done with iterator");
02171          continue;
02172       }
02173       ao2_lock(q);
02174       if (q->count && q->members) {
02175          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02176             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02177             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02178                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);
02179                found = 1;
02180             }
02181             ao2_ref(mem, -1);
02182          }
02183       }
02184       ao2_unlock(q);
02185       queue_t_unref(q, "Done with iterator");
02186       if (found) {
02187          break;
02188       }
02189    }
02190    ao2_iterator_destroy(&queue_iter);
02191    return found;
02192 }

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

Definition at line 5926 of file app_queue.c.

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

Referenced by complete_queue_add_member(), complete_queue_pause_member(), complete_queue_remove_member(), complete_queue_set_member_penalty(), and complete_queue_show().

05927 {
05928    struct call_queue *q;
05929    char *ret = NULL;
05930    int which = 0;
05931    int wordlen = strlen(word);
05932    struct ao2_iterator queue_iter;
05933 
05934    queue_iter = ao2_iterator_init(queues, 0);
05935    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05936       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05937          ret = ast_strdup(q->name);
05938          queue_t_unref(q, "Done with iterator");
05939          break;
05940       }
05941       queue_t_unref(q, "Done with iterator");
05942    }
05943    ao2_iterator_destroy(&queue_iter);
05944 
05945    return ret;
05946 }

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

Definition at line 6308 of file app_queue.c.

References ast_malloc, ast_strdup, complete_queue(), and num.

Referenced by handle_queue_add_member().

06309 {
06310    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
06311    switch (pos) {
06312    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
06313       return NULL;
06314    case 4: /* only one possible match, "to" */
06315       return state == 0 ? ast_strdup("to") : NULL;
06316    case 5: /* <queue> */
06317       return complete_queue(line, word, pos, state);
06318    case 6: /* only one possible match, "penalty" */
06319       return state == 0 ? ast_strdup("penalty") : NULL;
06320    case 7:
06321       if (state < 100) {      /* 0-99 */
06322          char *num;
06323          if ((num = ast_malloc(3))) {
06324             sprintf(num, "%d", state);
06325          }
06326          return num;
06327       } else {
06328          return NULL;
06329       }
06330    case 8: /* only one possible match, "as" */
06331       return state == 0 ? ast_strdup("as") : NULL;
06332    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
06333       return NULL;
06334    default:
06335       return NULL;
06336    }
06337 }

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

Definition at line 6526 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

06527 {
06528    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
06529    switch (pos) {
06530    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
06531       return NULL;
06532    case 4:  /* only one possible match, "queue" */
06533       return state == 0 ? ast_strdup("queue") : NULL;
06534    case 5:  /* <queue> */
06535       return complete_queue(line, word, pos, state);
06536    case 6: /* "reason" */
06537       return state == 0 ? ast_strdup("reason") : NULL;
06538    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
06539       return NULL;
06540    default:
06541       return NULL;
06542    }
06543 }

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

Definition at line 6438 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_strdup, complete_queue(), member::interface, member::membername, call_queue::members, queue_t_unref, and queues.

Referenced by handle_queue_remove_member().

06439 {
06440    int which = 0;
06441    struct call_queue *q;
06442    struct member *m;
06443    struct ao2_iterator queue_iter;
06444    struct ao2_iterator mem_iter;
06445    int wordlen = strlen(word);
06446 
06447    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
06448    if (pos > 5 || pos < 3)
06449       return NULL;
06450    if (pos == 4)   /* only one possible match, 'from' */
06451       return (state == 0 ? ast_strdup("from") : NULL);
06452 
06453    if (pos == 5)   /* No need to duplicate code */
06454       return complete_queue(line, word, pos, state);
06455 
06456    /* here is the case for 3, <member> */
06457    queue_iter = ao2_iterator_init(queues, 0);
06458    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06459       ao2_lock(q);
06460       mem_iter = ao2_iterator_init(q->members, 0);
06461       while ((m = ao2_iterator_next(&mem_iter))) {
06462          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06463             char *tmp;
06464             ao2_unlock(q);
06465             tmp = ast_strdup(m->interface);
06466             ao2_ref(m, -1);
06467             queue_t_unref(q, "Done with iterator");
06468             ao2_iterator_destroy(&mem_iter);
06469             ao2_iterator_destroy(&queue_iter);
06470             return tmp;
06471          }
06472          ao2_ref(m, -1);
06473       }
06474       ao2_iterator_destroy(&mem_iter);
06475       ao2_unlock(q);
06476       queue_t_unref(q, "Done with iterator");
06477    }
06478    ao2_iterator_destroy(&queue_iter);
06479 
06480    return NULL;
06481 }

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

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

06660 {
06661    int which = 0;
06662    struct rule_list *rl_iter;
06663    int wordlen = strlen(word);
06664    char *ret = NULL;
06665    if (pos != 3) /* Wha? */ {
06666       return NULL;
06667    }
06668 
06669    AST_LIST_LOCK(&rule_lists);
06670    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06671       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06672          ret = ast_strdup(rl_iter->name);
06673          break;
06674       }
06675    }
06676    AST_LIST_UNLOCK(&rule_lists);
06677 
06678    return ret;
06679 }

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

Definition at line 6596 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

06597 {
06598    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
06599    switch (pos) {
06600    case 4:
06601       if (state == 0) {
06602          return ast_strdup("on");
06603       } else {
06604          return NULL;
06605       }
06606    case 6:
06607       if (state == 0) {
06608          return ast_strdup("in");
06609       } else {
06610          return NULL;
06611       }
06612    case 7:
06613       return complete_queue(line, word, pos, state);
06614    default:
06615       return NULL;
06616    }
06617 }

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

Definition at line 5948 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

05949 {
05950    if (pos == 2)
05951       return complete_queue(line, word, pos, state);
05952    return NULL;
05953 }

static int compress_char ( const char  c  )  [static]

Definition at line 872 of file app_queue.c.

00873 {
00874    if (c < 32)
00875       return 0;
00876    else if (c > 96)
00877       return c - 64;
00878    else
00879       return c - 32;
00880 }

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

Copy rule from global list into specified queue.

Definition at line 4789 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(), 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::qe_rules, rule_list::rules, and penalty_rule::time.

Referenced by join_queue().

04790 {
04791    struct penalty_rule *pr_iter;
04792    struct rule_list *rl_iter;
04793    const char *tmp = rulename;
04794    if (ast_strlen_zero(tmp)) {
04795       return;
04796    }
04797    AST_LIST_LOCK(&rule_lists);
04798    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04799       if (!strcasecmp(rl_iter->name, tmp))
04800          break;
04801    }
04802    if (rl_iter) {
04803       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04804          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04805          if (!new_pr) {
04806             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04807             AST_LIST_UNLOCK(&rule_lists);
04808             break;
04809          }
04810          new_pr->time = pr_iter->time;
04811          new_pr->max_value = pr_iter->max_value;
04812          new_pr->min_value = pr_iter->min_value;
04813          new_pr->max_relative = pr_iter->max_relative;
04814          new_pr->min_relative = pr_iter->min_relative;
04815          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04816       }
04817    }
04818    AST_LIST_UNLOCK(&rule_lists);
04819 }

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

References ao2_alloc, ast_copy_string(), ast_device_state(), ast_log(), ast_strlen_zero(), LOG_WARNING, and member::penalty.

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

00848 {
00849    struct member *cur;
00850    
00851    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00852       cur->penalty = penalty;
00853       cur->paused = paused;
00854       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00855       if (!ast_strlen_zero(state_interface))
00856          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00857       else
00858          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00859       if (!ast_strlen_zero(membername))
00860          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00861       else
00862          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00863       if (!strchr(cur->interface, '/'))
00864          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00865       cur->status = ast_device_state(cur->state_interface);
00866    }
00867 
00868    return cur;
00869 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

01453 {
01454    struct call_queue *q = obj;
01455    int i;
01456 
01457    free_members(q, 1);
01458    ast_string_field_free_memory(q);
01459    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01460       if (q->sound_periodicannounce[i])
01461          free(q->sound_periodicannounce[i]);
01462    }
01463    ao2_ref(q->members, -1);
01464 }

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

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

00821 {
00822    enum ast_device_state state;
00823    const char *device;
00824    struct statechange *sc;
00825    size_t datapsize;
00826 
00827    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00828    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00829 
00830    if (ast_strlen_zero(device)) {
00831       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
00832       return;
00833    }
00834    datapsize = sizeof(*sc) + strlen(device) + 1;
00835    if (!(sc = ast_calloc(1, datapsize))) {
00836       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
00837       return;
00838    }
00839    sc->state = state;
00840    strcpy(sc->dev, device);
00841    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
00842       ast_free(sc);
00843    }
00844 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2195 of file app_queue.c.

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

Referenced by ring_entry().

02196 {
02197    o->stillgoing = 0;
02198    ast_hangup(o->chan);
02199    o->chan = NULL;
02200 }

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

References ast_cli(), astman_append(), and s.

Referenced by __queues_show().

05785 {
05786    if (s)
05787       astman_append(s, "%s\r\n", str);
05788    else
05789       ast_cli(fd, "%s\n", str);
05790 }

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 4155 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, and member::state_interface.

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

04156 {
04157    struct member *cur_member;
04158    char value[PM_MAX_LEN];
04159    int value_len = 0;
04160    int res;
04161    struct ao2_iterator mem_iter;
04162 
04163    memset(value, 0, sizeof(value));
04164 
04165    if (!pm_queue)
04166       return;
04167 
04168    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04169    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04170       if (!cur_member->dynamic) {
04171          ao2_ref(cur_member, -1);
04172          continue;
04173       }
04174 
04175       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04176          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04177 
04178       ao2_ref(cur_member, -1);
04179 
04180       if (res != strlen(value + value_len)) {
04181          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04182          break;
04183       }
04184       value_len += res;
04185    }
04186    ao2_iterator_destroy(&mem_iter);
04187    
04188    if (value_len && !cur_member) {
04189       if (ast_db_put(pm_family, pm_queue->name, value))
04190          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04191    } else
04192       /* Delete the entry if the queue is empty or there is an error */
04193       ast_db_del(pm_family, pm_queue->name);
04194 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 3336 of file app_queue.c.

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

03337 {
03338    struct queue_end_bridge *qeb = data;
03339    struct call_queue *q = qeb->q;
03340    struct ast_channel *chan = qeb->chan;
03341 
03342    if (ao2_ref(qeb, -1) == 1) {
03343       ao2_lock(q);
03344       set_queue_variables(q, chan);
03345       ao2_unlock(q);
03346       /* This unrefs the reference we made in try_calling when we allocated qeb */
03347       queue_t_unref(q, "Expire bridge_config reference");
03348    }
03349 }

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

Definition at line 3329 of file app_queue.c.

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

03330 {
03331    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03332    ao2_ref(qeb, +1);
03333    qeb->chan = originator;
03334 }

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

find the entry with the best metric, or NULL

Definition at line 2407 of file app_queue.c.

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

02408 {
02409    struct callattempt *best = NULL, *cur;
02410 
02411    for (cur = outgoing; cur; cur = cur->q_next) {
02412       if (cur->stillgoing &&              /* Not already done */
02413          !cur->chan &&              /* Isn't already going */
02414          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02415          best = cur;
02416       }
02417    }
02418 
02419    return best;
02420 }

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

01491 {
01492    struct ast_variable *v;
01493    struct call_queue *q, tmpq = {
01494       .name = queuename,   
01495    };
01496    struct member *m;
01497    struct ao2_iterator mem_iter;
01498    char *interface = NULL;
01499    const char *tmp_name;
01500    char *tmp;
01501    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01502 
01503    /* Static queues override realtime. */
01504    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) {
01505       ao2_lock(q);
01506       if (!q->realtime) {
01507          if (q->dead) {
01508             ao2_unlock(q);
01509             queue_t_unref(q, "Queue is dead; can't return it");
01510             return NULL;
01511          } else {
01512             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01513             ao2_unlock(q);
01514             return q;
01515          }
01516       }
01517    } else if (!member_config)
01518       /* Not found in the list, and it's not realtime ... */
01519       return NULL;
01520 
01521    /* Check if queue is defined in realtime. */
01522    if (!queue_vars) {
01523       /* Delete queue from in-core list if it has been deleted in realtime. */
01524       if (q) {
01525          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01526             found condition... So we might delete an in-core queue
01527             in case of DB failure. */
01528          ast_debug(1, "Queue %s not found in realtime.\n", queuename);
01529 
01530          q->dead = 1;
01531          /* Delete if unused (else will be deleted when last caller leaves). */
01532          queues_t_unlink(queues, q, "Unused, removing from container");
01533          ao2_unlock(q);
01534          queue_t_unref(q, "Queue is dead; can't return it");
01535       }
01536       return NULL;
01537    }
01538 
01539    /* Create a new queue if an in-core entry does not exist yet. */
01540    if (!q) {
01541       struct ast_variable *tmpvar = NULL;
01542       if (!(q = alloc_queue(queuename)))
01543          return NULL;
01544       ao2_lock(q);
01545       clear_queue(q);
01546       q->realtime = 1;
01547       /*Before we initialize the queue, we need to set the strategy, so that linear strategy
01548        * will allocate the members properly
01549        */
01550       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01551          if (!strcasecmp(tmpvar->name, "strategy")) {
01552             q->strategy = strat2int(tmpvar->value);
01553             if (q->strategy < 0) {
01554                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01555                tmpvar->value, q->name);
01556                q->strategy = QUEUE_STRATEGY_RINGALL;
01557             }
01558             break;
01559          }
01560       }
01561       /* We traversed all variables and didn't find a strategy */
01562       if (!tmpvar)
01563          q->strategy = QUEUE_STRATEGY_RINGALL;
01564       queues_t_link(queues, q, "Add queue to container");
01565    }
01566    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01567 
01568    memset(tmpbuf, 0, sizeof(tmpbuf));
01569    for (v = queue_vars; v; v = v->next) {
01570       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01571       if ((tmp = strchr(v->name, '_'))) {
01572          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01573          tmp_name = tmpbuf;
01574          tmp = tmpbuf;
01575          while ((tmp = strchr(tmp, '_')))
01576             *tmp++ = '-';
01577       } else
01578          tmp_name = v->name;
01579 
01580       if (!ast_strlen_zero(v->value)) {
01581          /* Don't want to try to set the option if the value is empty */
01582          queue_set_param(q, tmp_name, v->value, -1, 0);
01583       }
01584    }
01585 
01586    /* Temporarily set realtime members dead so we can detect deleted ones. 
01587     * Also set the membercount correctly for realtime*/
01588    mem_iter = ao2_iterator_init(q->members, 0);
01589    while ((m = ao2_iterator_next(&mem_iter))) {
01590       q->membercount++;
01591       if (m->realtime)
01592          m->dead = 1;
01593       ao2_ref(m, -1);
01594    }
01595    ao2_iterator_destroy(&mem_iter);
01596 
01597    while ((interface = ast_category_browse(member_config, interface))) {
01598       rt_handle_member_record(q, interface,
01599          ast_variable_retrieve(member_config, interface, "uniqueid"),
01600          S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface),
01601          ast_variable_retrieve(member_config, interface, "penalty"),
01602          ast_variable_retrieve(member_config, interface, "paused"),
01603          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01604    }
01605 
01606    /* Delete all realtime members that have been deleted in DB. */
01607    mem_iter = ao2_iterator_init(q->members, 0);
01608    while ((m = ao2_iterator_next(&mem_iter))) {
01609       if (m->dead) {
01610          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01611          ao2_unlink(q->members, m);
01612          remove_from_interfaces(m->state_interface, 0);
01613          q->membercount--;
01614       }
01615       ao2_ref(m, -1);
01616    }
01617    ao2_iterator_destroy(&mem_iter);
01618 
01619    ao2_unlock(q);
01620 
01621    return q;
01622 }

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

Iterate through queue's member list and delete them.

Definition at line 1434 of file app_queue.c.

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

Referenced by destroy_queue().

01435 {
01436    /* Free non-dynamic members */
01437    struct member *cur;
01438    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01439 
01440    while ((cur = ao2_iterator_next(&mem_iter))) {
01441       if (all || !cur->dynamic) {
01442          ao2_unlink(q->members, cur);
01443          remove_from_interfaces(cur->state_interface, 1);
01444          q->membercount--;
01445       }
01446       ao2_ref(cur, -1);
01447    }
01448    ao2_iterator_destroy(&mem_iter);
01449 }

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

Definition at line 4436 of file app_queue.c.

References ao2_lock(), ao2_ref, ao2_t_find, ao2_unlock(), ast_log(), interface_exists(), LOG_ERROR, OBJ_POINTER, member::penalty, queue_t_unref, queues, and RESULT_FAILURE.

Referenced by queue_function_memberpenalty_read().

04437 {
04438    int foundqueue = 0, penalty;
04439    struct call_queue *q, tmpq = {
04440       .name = queuename,   
04441    };
04442    struct member *mem;
04443    
04444    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) {
04445       foundqueue = 1;
04446       ao2_lock(q);
04447       if ((mem = interface_exists(q, interface))) {
04448          penalty = mem->penalty;
04449          ao2_ref(mem, -1);
04450          ao2_unlock(q);
04451          queue_t_unref(q, "Search complete");
04452          return penalty;
04453       }
04454       ao2_unlock(q);
04455       queue_t_unref(q, "Search complete");
04456    }
04457 
04458    /* some useful debuging */
04459    if (foundqueue) 
04460       ast_log (LOG_ERROR, "Invalid queuename\n");
04461    else 
04462       ast_log (LOG_ERROR, "Invalid interface\n");
04463 
04464    return RESULT_FAILURE;
04465 }

static enum queue_member_status get_member_status ( struct call_queue q,
int  max_penalty,
int  min_penalty 
) [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 QUEUE_NORMAL. If no members are available, the appropriate reason why is returned

Definition at line 687 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, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, and member::status.

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

00688 {
00689    struct member *member;
00690    struct ao2_iterator mem_iter;
00691    enum queue_member_status result = QUEUE_NO_MEMBERS;
00692 
00693    ao2_lock(q);
00694    mem_iter = ao2_iterator_init(q->members, 0);
00695    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00696       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
00697          continue;
00698 
00699       switch (member->status) {
00700       case AST_DEVICE_INVALID:
00701          /* nothing to do */
00702          break;
00703       case AST_DEVICE_UNAVAILABLE:
00704          if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS) 
00705             result = QUEUE_NO_REACHABLE_MEMBERS;
00706          break;
00707       default:
00708          if (member->paused) {
00709             result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
00710          } else {
00711             ao2_unlock(q);
00712             ao2_ref(member, -1);
00713             ao2_iterator_destroy(&mem_iter);
00714             return QUEUE_NORMAL;
00715          }
00716          break;
00717       }
00718    }
00719    ao2_iterator_destroy(&mem_iter);
00720 
00721    ao2_unlock(q);
00722    return result;
00723 }

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

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

06365 {
06366    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06367    int penalty;
06368 
06369    switch ( cmd ) {
06370    case CLI_INIT:
06371       e->command = "queue add member";
06372       e->usage =
06373          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06374          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
06375       return NULL;
06376    case CLI_GENERATE:
06377       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06378    }
06379 
06380    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06381       return CLI_SHOWUSAGE;
06382    } else if (strcmp(a->argv[4], "to")) {
06383       return CLI_SHOWUSAGE;
06384    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06385       return CLI_SHOWUSAGE;
06386    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06387       return CLI_SHOWUSAGE;
06388    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06389       return CLI_SHOWUSAGE;
06390    }
06391 
06392    queuename = a->argv[5];
06393    interface = a->argv[3];
06394    if (a->argc >= 8) {
06395       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
06396          if (penalty < 0) {
06397             ast_cli(a->fd, "Penalty must be >= 0\n");
06398             penalty = 0;
06399          }
06400       } else {
06401          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06402          penalty = 0;
06403       }
06404    } else {
06405       penalty = 0;
06406    }
06407 
06408    if (a->argc >= 10) {
06409       membername = a->argv[9];
06410    }
06411 
06412    if (a->argc >= 12) {
06413       state_interface = a->argv[11];
06414    }
06415 
06416    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06417    case RES_OKAY:
06418       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06419       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06420       return CLI_SUCCESS;
06421    case RES_EXISTS:
06422       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06423       return CLI_FAILURE;
06424    case RES_NOSUCHQUEUE:
06425       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06426       return CLI_FAILURE;
06427    case RES_OUTOFMEMORY:
06428       ast_cli(a->fd, "Out of memory\n");
06429       return CLI_FAILURE;
06430    case RES_NOT_DYNAMIC:
06431       ast_cli(a->fd, "Member not dynamic\n");
06432       return CLI_FAILURE;
06433    default:
06434       return CLI_FAILURE;
06435    }
06436 }

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

Definition at line 6545 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(), and ast_cli_entry::usage.

06546 {
06547    char *queuename, *interface, *reason;
06548    int paused;
06549 
06550    switch (cmd) {
06551    case CLI_INIT:
06552       e->command = "queue {pause|unpause} member";
06553       e->usage = 
06554          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06555          "  Pause or unpause a queue member. Not specifying a particular queue\n"
06556          "  will pause or unpause a member across all queues to which the member\n"
06557          "  belongs.\n";
06558       return NULL;
06559    case CLI_GENERATE:
06560       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06561    }
06562 
06563    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06564       return CLI_SHOWUSAGE;
06565    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06566       return CLI_SHOWUSAGE;
06567    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06568       return CLI_SHOWUSAGE;
06569    }
06570 
06571 
06572    interface = a->argv[3];
06573    queuename = a->argc >= 6 ? a->argv[5] : NULL;
06574    reason = a->argc == 8 ? a->argv[7] : NULL;
06575    paused = !strcasecmp(a->argv[1], "pause");
06576 
06577    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06578       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06579       if (!ast_strlen_zero(queuename))
06580          ast_cli(a->fd, " in queue '%s'", queuename);
06581       if (!ast_strlen_zero(reason))
06582          ast_cli(a->fd, " for reason '%s'", reason);
06583       ast_cli(a->fd, "\n");
06584       return CLI_SUCCESS;
06585    } else {
06586       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06587       if (!ast_strlen_zero(queuename))
06588          ast_cli(a->fd, " in queue '%s'", queuename);
06589       if (!ast_strlen_zero(reason))
06590          ast_cli(a->fd, " for reason '%s'", reason);
06591       ast_cli(a->fd, "\n");
06592       return CLI_FAILURE;
06593    }
06594 }

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

Definition at line 6483 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_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.

06484 {
06485    char *queuename, *interface;
06486 
06487    switch (cmd) {
06488    case CLI_INIT:
06489       e->command = "queue remove member";
06490       e->usage = 
06491          "Usage: queue remove member <channel> from <queue>\n"
06492          "       Remove a specific channel from a queue.\n";
06493       return NULL;
06494    case CLI_GENERATE:
06495       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06496    }
06497 
06498    if (a->argc != 6) {
06499       return CLI_SHOWUSAGE;
06500    } else if (strcmp(a->argv[4], "from")) {
06501       return CLI_SHOWUSAGE;
06502    }
06503 
06504    queuename = a->argv[5];
06505    interface = a->argv[3];
06506 
06507    switch (remove_from_queue(queuename, interface)) {
06508    case RES_OKAY:
06509       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06510       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06511       return CLI_SUCCESS;
06512    case RES_EXISTS:
06513       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06514       return CLI_FAILURE;
06515    case RES_NOSUCHQUEUE:
06516       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06517       return CLI_FAILURE;
06518    case RES_OUTOFMEMORY:
06519       ast_cli(a->fd, "Out of memory\n");
06520       return CLI_FAILURE;
06521    default:
06522       return CLI_FAILURE;
06523    }
06524 }

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

Definition at line 6715 of file app_queue.c.

References CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, reload_queue_rules(), and ast_cli_entry::usage.

06716 {
06717    switch (cmd) {
06718       case CLI_INIT:
06719          e->command = "queue rules reload";
06720          e->usage = 
06721             "Usage: queue rules reload\n"
06722             "  Reloads rules defined in queuerules.conf\n";
06723          return NULL;
06724       case CLI_GENERATE:
06725          return NULL;
06726    }
06727    reload_queue_rules(1);
06728    return CLI_SUCCESS;
06729 }

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

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

06682 {
06683    char *rule;
06684    struct rule_list *rl_iter;
06685    struct penalty_rule *pr_iter;
06686    switch (cmd) {
06687    case CLI_INIT:
06688       e->command = "queue rules show";
06689       e->usage =
06690       "Usage: queue rules show [rulename]\n"
06691       "  Show the list of rules associated with rulename. If no\n"
06692       "  rulename is specified, list all rules defined in queuerules.conf\n";
06693       return NULL;
06694    case CLI_GENERATE:
06695       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06696    }
06697 
06698    if (a->argc != 3 && a->argc != 4)
06699       return CLI_SHOWUSAGE;
06700 
06701    rule = a->argc == 4 ? a->argv[3] : "";
06702    AST_LIST_LOCK(&rule_lists);
06703    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06704       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06705          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06706          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06707             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);
06708          }
06709       }
06710    }
06711    AST_LIST_UNLOCK(&rule_lists);
06712    return CLI_SUCCESS; 
06713 }

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

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

06620 {
06621    char *queuename = NULL, *interface;
06622    int penalty = 0;
06623 
06624    switch (cmd) {
06625    case CLI_INIT:
06626       e->command = "queue set penalty";
06627       e->usage = 
06628       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06629       "  Set a member's penalty in the queue specified. If no queue is specified\n"
06630       "  then that interface's penalty is set in all queues to which that interface is a member\n";
06631       return NULL;
06632    case CLI_GENERATE:
06633       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06634    }
06635 
06636    if (a->argc != 6 && a->argc != 8) {
06637       return CLI_SHOWUSAGE;
06638    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06639       return CLI_SHOWUSAGE;
06640    }
06641 
06642    if (a->argc == 8)
06643       queuename = a->argv[7];
06644    interface = a->argv[5];
06645    penalty = atoi(a->argv[3]);
06646 
06647    switch (set_member_penalty(queuename, interface, penalty)) {
06648    case RESULT_SUCCESS:
06649       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06650       return CLI_SUCCESS;
06651    case RESULT_FAILURE:
06652       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06653       return CLI_FAILURE;
06654    default:
06655       return CLI_FAILURE;
06656    }
06657 }

static int handle_statechange ( void *  datap  )  [static]

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

Definition at line 789 of file app_queue.c.

References ast_copy_string(), ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, statechange::dev, devstate2str(), member_interface::interface, statechange::state, and update_status().

00790 {
00791    struct member_interface *curint;
00792    struct statechange *sc = datap;
00793    char interface[80];
00794 
00795    AST_LIST_LOCK(&interfaces);
00796    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00797       char *slash_pos;
00798       ast_copy_string(interface, curint->interface, sizeof(interface));
00799       if ((slash_pos = strchr(interface, '/')))
00800          if ((slash_pos = strchr(slash_pos + 1, '/')))
00801             *slash_pos = '\0';
00802       if (!strcasecmp(interface, sc->dev))
00803          break;
00804    }
00805    AST_LIST_UNLOCK(&interfaces);
00806 
00807    if (!curint) {
00808       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, devstate2str(sc->state));
00809       ast_free(sc);
00810       return 0;
00811    }
00812 
00813    ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00814 
00815    update_status(sc->dev, sc->state);
00816    ast_free(sc);
00817    return 0;
00818 }

static void hangupcalls ( struct callattempt outgoing,
struct ast_channel exception 
) [static]

Hang up a list of outgoing calls.

Definition at line 2091 of file app_queue.c.

References ao2_ref, ast_free, ast_hangup(), callattempt::chan, callattempt::member, and callattempt::q_next.

02092 {
02093    struct callattempt *oo;
02094 
02095    while (outgoing) {
02096       /* Hangup any existing lines we have open */
02097       if (outgoing->chan && (outgoing->chan != exception))
02098          ast_hangup(outgoing->chan);
02099       oo = outgoing;
02100       outgoing = outgoing->q_next;
02101       if (oo->member)
02102          ao2_ref(oo->member, -1);
02103       ast_free(oo);
02104    }
02105 }

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 904 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::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::membercount, call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_LINEAR, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

00905 {
00906    int i;
00907    struct penalty_rule *pr_iter;
00908 
00909    q->dead = 0;
00910    q->retry = DEFAULT_RETRY;
00911    q->timeout = DEFAULT_TIMEOUT;
00912    q->maxlen = 0;
00913    q->announcefrequency = 0;
00914    q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY;
00915    q->announceholdtime = 1;
00916    q->announcepositionlimit = 10; /* Default 10 positions */
00917    q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */
00918    q->roundingseconds = 0; /* Default - don't announce seconds */
00919    q->servicelevel = 0;
00920    q->ringinuse = 1;
00921    q->setinterfacevar = 0;
00922    q->setqueuevar = 0;
00923    q->setqueueentryvar = 0;
00924    q->autofill = autofill_default;
00925    q->montype = montype_default;
00926    q->monfmt[0] = '\0';
00927    q->reportholdtime = 0;
00928    q->wrapuptime = 0;
00929    q->joinempty = 0;
00930    q->leavewhenempty = 0;
00931    q->memberdelay = 0;
00932    q->maskmemberstatus = 0;
00933    q->eventwhencalled = 0;
00934    q->weight = 0;
00935    q->timeoutrestart = 0;
00936    q->periodicannouncefrequency = 0;
00937    q->randomperiodicannounce = 0;
00938    q->numperiodicannounce = 0;
00939    q->timeoutpriority = TIMEOUT_PRIORITY_APP;
00940    if (!q->members) {
00941       if (q->strategy == QUEUE_STRATEGY_LINEAR)
00942          /* linear strategy depends on order, so we have to place all members in a single bucket */
00943          q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
00944       else
00945          q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00946    }
00947    q->membercount = 0;
00948    q->found = 1;
00949 
00950    ast_string_field_set(q, sound_next, "queue-youarenext");
00951    ast_string_field_set(q, sound_thereare, "queue-thereare");
00952    ast_string_field_set(q, sound_calls, "queue-callswaiting");
00953    ast_string_field_set(q, queue_quantity1, "queue-quantity1");
00954    ast_string_field_set(q, queue_quantity2, "queue-quantity2");
00955    ast_string_field_set(q, sound_holdtime, "queue-holdtime");
00956    ast_string_field_set(q, sound_minutes, "queue-minutes");
00957    ast_string_field_set(q, sound_minute, "queue-minute");
00958    ast_string_field_set(q, sound_seconds, "queue-seconds");
00959    ast_string_field_set(q, sound_thanks, "queue-thankyou");
00960    ast_string_field_set(q, sound_reporthold, "queue-reporthold");
00961 
00962    if ((q->sound_periodicannounce[0] = ast_str_create(32)))
00963       ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce");
00964 
00965    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00966       if (q->sound_periodicannounce[i])
00967          ast_str_set(&q->sound_periodicannounce[i], 0, "%s", "");
00968    }
00969 
00970    while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list)))
00971       ast_free(pr_iter);
00972 }

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

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

Referenced by join_queue().

00651 {
00652    struct queue_ent *cur;
00653 
00654    if (!q || !new)
00655       return;
00656    if (prev) {
00657       cur = prev->next;
00658       prev->next = new;
00659    } else {
00660       cur = q->head;
00661       q->head = new;
00662    }
00663    new->next = cur;
00664 
00665    /* every queue_ent must have a reference to it's parent call_queue, this
00666     * reference does not go away until the end of the queue_ent's life, meaning
00667     * that even when the queue_ent leaves the call_queue this ref must remain. */
00668    queue_ref(q);
00669    new->parent = q;
00670    new->pos = ++(*pos);
00671    new->opos = *pos;
00672 }

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 1078 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_ERROR, LOG_WARNING, rule_list::name, and rule_list::rules.

Referenced by reload_queue_rules().

01079 {
01080    char *timestr, *maxstr, *minstr, *contentdup;
01081    struct penalty_rule *rule = NULL, *rule_iter;
01082    struct rule_list *rl_iter;
01083    int penaltychangetime, inserted = 0;
01084 
01085    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01086       ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
01087       return -1;
01088    }
01089 
01090    contentdup = ast_strdupa(content);
01091    
01092    if (!(maxstr = strchr(contentdup, ','))) {
01093       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01094       ast_free(rule);
01095       return -1;
01096    }
01097 
01098    *maxstr++ = '\0';
01099    timestr = contentdup;
01100 
01101    if ((penaltychangetime = atoi(timestr)) < 0) {
01102       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01103       ast_free(rule);
01104       return -1;
01105    }
01106 
01107    rule->time = penaltychangetime;
01108 
01109    if ((minstr = strchr(maxstr,',')))
01110       *minstr++ = '\0';
01111    
01112    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01113     * OR if a min penalty change is indicated but no max penalty change is */
01114    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01115       rule->max_relative = 1;
01116    }
01117 
01118    rule->max_value = atoi(maxstr);
01119 
01120    if (!ast_strlen_zero(minstr)) {
01121       if (*minstr == '+' || *minstr == '-')
01122          rule->min_relative = 1;
01123       rule->min_value = atoi(minstr);
01124    } else /*there was no minimum specified, so assume this means no change*/
01125       rule->min_relative = 1;
01126 
01127    /*We have the rule made, now we need to insert it where it belongs*/
01128    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01129       if (strcasecmp(rl_iter->name, list_name))
01130          continue;
01131 
01132       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01133          if (rule->time < rule_iter->time) {
01134             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01135             inserted = 1;
01136             break;
01137          }
01138       }
01139       AST_LIST_TRAVERSE_SAFE_END;
01140    
01141       if (!inserted) {
01142          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01143       }
01144    }
01145 
01146    return 0;
01147 }

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

Definition at line 568 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

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

00569 {
00570    int x;
00571 
00572    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00573       if (strategy == strategies[x].strategy)
00574          return strategies[x].name;
00575    }
00576 
00577    return "<unknown>";
00578 }

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

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

04130 {
04131    struct member *mem;
04132    struct ao2_iterator mem_iter;
04133 
04134    if (!q)
04135       return NULL;
04136 
04137    mem_iter = ao2_iterator_init(q->members, 0);
04138    while ((mem = ao2_iterator_next(&mem_iter))) {
04139       if (!strcasecmp(interface, mem->interface)) {
04140          ao2_iterator_destroy(&mem_iter);
04141          return mem;
04142       }
04143       ao2_ref(mem, -1);
04144    }
04145    ao2_iterator_destroy(&mem_iter);
04146 
04147    return NULL;
04148 }

static int interface_exists_global ( const char *  interface,
int  lock_queue_container 
) [static]

Definition at line 1009 of file app_queue.c.

References ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_copy_string(), member::interface, call_queue::members, queue_t_unref, queues, and member::state_interface.

Referenced by remove_from_interfaces().

01010 {
01011    struct call_queue *q;
01012    struct member *mem, tmpmem;
01013    struct ao2_iterator queue_iter, mem_iter;
01014    int ret = 0;
01015 
01016    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01017    queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : AO2_ITERATOR_DONTLOCK);
01018    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
01019       ao2_lock(q);
01020       mem_iter = ao2_iterator_init(q->members, 0);
01021       while ((mem = ao2_iterator_next(&mem_iter))) { 
01022          if (!strcasecmp(mem->state_interface, interface)) {
01023             ao2_ref(mem, -1);
01024             ret = 1;
01025             break;
01026          }
01027       }
01028       ao2_iterator_destroy(&mem_iter);
01029       ao2_unlock(q);
01030       queue_t_unref(q, "Done with iterator");
01031    }
01032    ao2_iterator_destroy(&queue_iter);
01033 
01034    return ret;
01035 }

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

02902 {
02903    struct queue_ent *ch;
02904    int res;
02905    int avl;
02906    int idx = 0;
02907    /* This needs a lock. How many members are available to be served? */
02908    ao2_lock(qe->parent);
02909 
02910    avl = num_available_members(qe->parent);
02911 
02912    ch = qe->parent->head;
02913 
02914    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02915 
02916    while ((idx < avl) && (ch) && (ch != qe)) {
02917       if (!ch->pending)
02918          idx++;
02919       ch = ch->next;       
02920    }
02921 
02922    ao2_unlock(qe->parent);
02923    /* If the queue entry is within avl [the number of available members] calls from the top ... 
02924     * Autofill and position check added to support autofill=no (as only calls
02925     * from the front of the queue are valid when autofill is disabled)
02926     */
02927    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02928       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02929       res = 1;
02930    } else {
02931       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02932       res = 0;
02933    }
02934 
02935    return res;
02936 }

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

Definition at line 1734 of file app_queue.c.

References queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), ast_debug, AST_LIST_FIRST, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, queue_ent::context, copy_rules(), call_queue::count, call_queue::defaultrule, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), manager_event, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, queue_ent::moh, ast_channel::name, queue_ent::next, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, queues, S_OR, status, ast_channel::uniqueid, and update_qe_rule().

Referenced by queue_exec().

01735 {
01736    struct call_queue *q;
01737    struct queue_ent *cur, *prev = NULL;
01738    int res = -1;
01739    int pos = 0;
01740    int inserted = 0;
01741    enum queue_member_status status;
01742    int exit = 0;
01743 
01744    if (!(q = load_realtime_queue(queuename)))
01745       return res;
01746 
01747    ao2_lock(queues);
01748    ao2_lock(q);
01749 
01750    copy_rules(qe, S_OR(overriding_rule, q->defaultrule));
01751    qe->pr = AST_LIST_FIRST(&qe->qe_rules);
01752 
01753    /* This is our one */
01754    while (!exit) {
01755       status = get_member_status(q, qe->max_penalty, qe->min_penalty);
01756       if (!q->joinempty && (status == QUEUE_NO_MEMBERS))
01757          *reason = QUEUE_JOINEMPTY;
01758       else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01759          *reason = QUEUE_JOINUNAVAIL;
01760       else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01761          *reason = QUEUE_JOINUNAVAIL;
01762       else if (q->maxlen && (q->count >= q->maxlen))
01763          *reason = QUEUE_FULL;
01764       else {
01765          /* There's space for us, put us at the right position inside
01766           * the queue.
01767           * Take into account the priority of the calling user */
01768          inserted = 0;
01769          prev = NULL;
01770          cur = q->head;
01771          while (cur) {
01772             /* We have higher priority than the current user, enter
01773              * before him, after all the other users with priority
01774              * higher or equal to our priority. */
01775             if ((!inserted) && (qe->prio > cur->prio)) {
01776                insert_entry(q, prev, qe, &pos);
01777                inserted = 1;
01778             }
01779             cur->pos = ++pos;
01780             prev = cur;
01781             cur = cur->next;
01782          }
01783          /* No luck, join at the end of the queue */
01784          if (!inserted)
01785             insert_entry(q, prev, qe, &pos);
01786          ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01787          ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01788          ast_copy_string(qe->context, q->context, sizeof(qe->context));
01789          q->count++;
01790          res = 0;
01791          manager_event(EVENT_FLAG_CALL, "Join",
01792             "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01793             qe->chan->name,
01794             S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01795             S_OR(qe->chan->cid.cid_name, "unknown"),
01796             q->name, qe->pos, q->count, qe->chan->uniqueid );
01797          ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01798       }
01799       if (!exit && qe->pr && res) {
01800          /* We failed to join the queue, but perhaps we can join if we move
01801           * to the next defined penalty rule
01802           */
01803          update_qe_rule(qe);
01804       } else {
01805          exit = 1;
01806       }
01807    }
01808    ao2_unlock(q);
01809    ao2_unlock(queues);
01810 
01811    return res;
01812 }

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

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

02035 {
02036    struct call_queue *q;
02037    struct queue_ent *current, *prev = NULL;
02038    struct penalty_rule *pr_iter;
02039    int pos = 0;
02040 
02041    if (!(q = qe->parent))
02042       return;
02043    queue_t_ref(q, "Copy queue pointer from queue entry");
02044    ao2_lock(q);
02045 
02046    prev = NULL;
02047    for (current = q->head; current; current = current->next) {
02048       if (current == qe) {
02049          q->count--;
02050 
02051          /* Take us out of the queue */
02052          manager_event(EVENT_FLAG_CALL, "Leave",
02053             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02054             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
02055          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02056          /* Take us out of the queue */
02057          if (prev)
02058             prev->next = current->next;
02059          else
02060             q->head = current->next;
02061          /* Free penalty rules */
02062          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02063             ast_free(pr_iter);
02064       } else {
02065          /* Renumber the people after us in the queue based on a new count */
02066          current->pos = ++pos;
02067          prev = current;
02068       }
02069    }
02070    ao2_unlock(q);
02071 
02072    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02073    if (q->realtime) {
02074       struct ast_variable *var;
02075       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02076          q->dead = 1;
02077       } else {
02078          ast_variables_destroy(var);
02079       }
02080    }
02081 
02082    if (q->dead) { 
02083       /* It's dead and nobody is in it, so kill it */
02084       queues_t_unlink(queues, q, "Queue is now dead; remove it from the container");
02085    }
02086    /* unref the explicit ref earlier in the function */
02087    queue_t_unref(q, "Expire copied reference");
02088 }

static int load_module ( void   )  [static]

Definition at line 6802 of file app_queue.c.

References ao2_container_alloc, aqm_exec(), ast_add_extension2(), ast_cli_register_multiple(), ast_context_find_or_create(), ast_custom_function_register, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), ast_free_ptr, ast_log(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application, ast_strdup, ast_taskprocessor_get(), cli_queue, device_state_cb(), device_state_sub, devicestate_tps, EVENT_FLAG_AGENT, LOG_ERROR, LOG_WARNING, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_log_custom(), manager_queue_member_penalty(), manager_queue_rule_show(), manager_queues_show(), manager_queues_status(), manager_queues_summary(), manager_remove_queue_member(), MAX_QUEUE_BUCKETS, pqm_exec(), ql_exec(), queue_cmp_cb(), queue_exec(), queue_hash_cb(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().

06803 {
06804    int res;
06805    struct ast_context *con;
06806 
06807    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06808 
06809    if (!reload_queues(0))
06810       return AST_MODULE_LOAD_DECLINE;
06811 
06812    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06813    if (!con)
06814       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06815    else
06816       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06817 
06818    if (queue_persistent_members)
06819       reload_queue_members();
06820 
06821    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06822    res = ast_register_application(app, queue_exec, synopsis, descrip);
06823    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06824    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06825    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06826    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06827    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06828    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06829    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06830    res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06831    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06832    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06833    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06834    res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06835    res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 
06836    res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06837    res |= ast_custom_function_register(&queuevar_function);
06838    res |= ast_custom_function_register(&queuemembercount_function);
06839    res |= ast_custom_function_register(&queuemembercount_dep);
06840    res |= ast_custom_function_register(&queuememberlist_function);
06841    res |= ast_custom_function_register(&queuewaitingcount_function);
06842    res |= ast_custom_function_register(&queuememberpenalty_function);
06843 
06844    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06845       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06846    }
06847 
06848    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06849       res = -1;
06850    }
06851 
06852    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06853 
06854    return res ? AST_MODULE_LOAD_DECLINE : 0;
06855 }

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

Definition at line 1624 of file app_queue.c.

References ao2_lock(), ao2_t_find, ao2_unlock(), 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, and update_realtime_members().

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

01625 {
01626    struct ast_variable *queue_vars;
01627    struct ast_config *member_config = NULL;
01628    struct call_queue *q = NULL, tmpq = {
01629       .name = queuename,   
01630    };
01631 
01632    /* Find the queue in the in-core list first. */
01633    q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first");
01634 
01635    if (!q || q->realtime) {
01636       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
01637          queue operations while waiting for the DB.
01638 
01639          This will be two separate database transactions, so we might
01640          see queue parameters as they were before another process
01641          changed the queue and member list as it was after the change.
01642          Thus we might see an empty member list when a queue is
01643          deleted. In practise, this is unlikely to cause a problem. */
01644 
01645       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
01646       if (queue_vars) {
01647          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
01648          if (!member_config) {
01649             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01650             ast_variables_destroy(queue_vars);
01651             return NULL;
01652          }
01653       }
01654 
01655       ao2_lock(queues);
01656       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01657       if (member_config)
01658          ast_config_destroy(member_config);
01659       if (queue_vars)
01660          ast_variables_destroy(queue_vars);
01661       ao2_unlock(queues);
01662 
01663    } else {
01664       update_realtime_members(q);
01665    }
01666    return q;
01667 }

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

Definition at line 6178 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, RES_OUTOFMEMORY, and s.

Referenced by load_module().

06179 {
06180    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06181    int paused, penalty = 0;
06182 
06183    queuename = astman_get_header(m, "Queue");
06184    interface = astman_get_header(m, "Interface");
06185    penalty_s = astman_get_header(m, "Penalty");
06186    paused_s = astman_get_header(m, "Paused");
06187    membername = astman_get_header(m, "MemberName");
06188    state_interface = astman_get_header(m, "StateInterface");
06189 
06190    if (ast_strlen_zero(queuename)) {
06191       astman_send_error(s, m, "'Queue' not specified.");
06192       return 0;
06193    }
06194 
06195    if (ast_strlen_zero(interface)) {
06196       astman_send_error(s, m, "'Interface' not specified.");
06197       return 0;
06198    }
06199 
06200    if (ast_strlen_zero(penalty_s))
06201       penalty = 0;
06202    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
06203       penalty = 0;
06204 
06205    if (ast_strlen_zero(paused_s))
06206       paused = 0;
06207    else
06208       paused = abs(ast_true(paused_s));
06209 
06210    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06211    case RES_OKAY:
06212       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06213       astman_send_ack(s, m, "Added interface to queue");
06214       break;
06215    case RES_EXISTS:
06216       astman_send_error(s, m, "Unable to add interface: Already there");
06217       break;
06218    case RES_NOSUCHQUEUE:
06219       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06220       break;
06221    case RES_OUTOFMEMORY:
06222       astman_send_error(s, m, "Out of memory");
06223       break;
06224    }
06225 
06226    return 0;
06227 }

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

Definition at line 6263 of file app_queue.c.

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

Referenced by load_module().

06264 {
06265    const char *queuename, *interface, *paused_s, *reason;
06266    int paused;
06267 
06268    interface = astman_get_header(m, "Interface");
06269    paused_s = astman_get_header(m, "Paused");
06270    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
06271    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
06272 
06273    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06274       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06275       return 0;
06276    }
06277 
06278    paused = abs(ast_true(paused_s));
06279 
06280    if (set_member_paused(queuename, interface, reason, paused))
06281       astman_send_error(s, m, "Interface not found");
06282    else
06283       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06284    return 0;
06285 }

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

Definition at line 6287 of file app_queue.c.

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

Referenced by load_module().

06288 {
06289    const char *queuename, *event, *message, *interface, *uniqueid;
06290 
06291    queuename = astman_get_header(m, "Queue");
06292    uniqueid = astman_get_header(m, "UniqueId");
06293    interface = astman_get_header(m, "Interface");
06294    event = astman_get_header(m, "Event");
06295    message = astman_get_header(m, "Message");
06296 
06297    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06298       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06299       return 0;
06300    }
06301 
06302    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06303    astman_send_ack(s, m, "Event added successfully");
06304 
06305    return 0;
06306 }

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

Definition at line 6339 of file app_queue.c.

References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_penalty().

Referenced by load_module().

06340 {
06341    const char *queuename, *interface, *penalty_s;
06342    int penalty;
06343 
06344    interface = astman_get_header(m, "Interface");
06345    penalty_s = astman_get_header(m, "Penalty");
06346    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
06347    queuename = astman_get_header(m, "Queue");
06348 
06349    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06350       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06351       return 0;
06352    }
06353  
06354    penalty = atoi(penalty_s);
06355 
06356    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06357       astman_send_error(s, m, "Invalid interface, queuename or penalty");
06358    else
06359       astman_send_ack(s, m, "Interface penalty set successfully");
06360 
06361    return 0;
06362 }

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

Definition at line 5984 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, s, and penalty_rule::time.

Referenced by load_module().

05985 {
05986    const char *rule = astman_get_header(m, "Rule");
05987    struct rule_list *rl_iter;
05988    struct penalty_rule *pr_iter;
05989 
05990    AST_LIST_LOCK(&rule_lists);
05991    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05992       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05993          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05994          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05995             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 );
05996          }
05997          if (!ast_strlen_zero(rule))
05998             break;
05999       }
06000    }
06001    AST_LIST_UNLOCK(&rule_lists);
06002 
06003    astman_append(s, "\r\n\r\n");
06004 
06005    return RESULT_SUCCESS;
06006 }

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

Definition at line 5974 of file app_queue.c.

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

Referenced by load_module().

05975 {
05976    char *a[] = { "queue", "show" };
05977 
05978    __queues_show(s, -1, 2, a);
05979    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05980 
05981    return RESULT_SUCCESS;
05982 }

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

Queue status info via AMI.

Definition at line 6083 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(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_t_unref, queues, RESULT_SUCCESS, s, S_OR, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by load_module().

06084 {
06085    time_t now;
06086    int pos;
06087    const char *id = astman_get_header(m,"ActionID");
06088    const char *queuefilter = astman_get_header(m,"Queue");
06089    const char *memberfilter = astman_get_header(m,"Member");
06090    char idText[256] = "";
06091    struct call_queue *q;
06092    struct queue_ent *qe;
06093    float sl = 0;
06094    struct member *mem;
06095    struct ao2_iterator queue_iter;
06096    struct ao2_iterator mem_iter;
06097 
06098    astman_send_ack(s, m, "Queue status will follow");
06099    time(&now);
06100    if (!ast_strlen_zero(id))
06101       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
06102 
06103    queue_iter = ao2_iterator_init(queues, 0);
06104    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06105       ao2_lock(q);
06106 
06107       /* List queue properties */
06108       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06109          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
06110          astman_append(s, "Event: QueueParams\r\n"
06111             "Queue: %s\r\n"
06112             "Max: %d\r\n"
06113             "Strategy: %s\r\n"
06114             "Calls: %d\r\n"
06115             "Holdtime: %d\r\n"
06116             "Completed: %d\r\n"
06117             "Abandoned: %d\r\n"
06118             "ServiceLevel: %d\r\n"
06119             "ServicelevelPerf: %2.1f\r\n"
06120             "Weight: %d\r\n"
06121             "%s"
06122             "\r\n",
06123             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06124             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06125          /* List Queue Members */
06126          mem_iter = ao2_iterator_init(q->members, 0);
06127          while ((mem = ao2_iterator_next(&mem_iter))) {
06128             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06129                astman_append(s, "Event: QueueMember\r\n"
06130                   "Queue: %s\r\n"
06131                   "Name: %s\r\n"
06132                   "Location: %s\r\n"
06133                   "Membership: %s\r\n"
06134                   "Penalty: %d\r\n"
06135                   "CallsTaken: %d\r\n"
06136                   "LastCall: %d\r\n"
06137                   "Status: %d\r\n"
06138                   "Paused: %d\r\n"
06139                   "%s"
06140                   "\r\n",
06141                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06142                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06143             }
06144             ao2_ref(mem, -1);
06145          }
06146          ao2_iterator_destroy(&mem_iter);
06147          /* List Queue Entries */
06148          pos = 1;
06149          for (qe = q->head; qe; qe = qe->next) {
06150             astman_append(s, "Event: QueueEntry\r\n"
06151                "Queue: %s\r\n"
06152                "Position: %d\r\n"
06153                "Channel: %s\r\n"
06154                "CallerIDNum: %s\r\n"
06155                "CallerIDName: %s\r\n"
06156                "Wait: %ld\r\n"
06157                "%s"
06158                "\r\n",
06159                q->name, pos++, qe->chan->name,
06160                S_OR(qe->chan->cid.cid_num, "unknown"),
06161                S_OR(qe->chan->cid.cid_name, "unknown"),
06162                (long) (now - qe->start), idText);
06163          }
06164       }
06165       ao2_unlock(q);
06166       queue_t_unref(q, "Done with iterator");
06167    }
06168    ao2_iterator_destroy(&queue_iter);
06169 
06170    astman_append(s,
06171       "Event: QueueStatusComplete\r\n"
06172       "%s"
06173       "\r\n",idText);
06174 
06175    return RESULT_SUCCESS;
06176 }

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

Summary of queue info via the AMI.

Definition at line 6009 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), call_queue::head, call_queue::holdtime, call_queue::members, call_queue::name, queue_ent::next, member::paused, queue_t_unref, queues, RESULT_SUCCESS, s, queue_ent::start, and member::status.

Referenced by load_module().

06010 {
06011    time_t now;
06012    int qmemcount = 0;
06013    int qmemavail = 0;
06014    int qchancount = 0;
06015    int qlongestholdtime = 0;
06016    const char *id = astman_get_header(m, "ActionID");
06017    const char *queuefilter = astman_get_header(m, "Queue");
06018    char idText[256] = "";
06019    struct call_queue *q;
06020    struct queue_ent *qe;
06021    struct member *mem;
06022    struct ao2_iterator queue_iter;
06023    struct ao2_iterator mem_iter;
06024 
06025    astman_send_ack(s, m, "Queue summary will follow");
06026    time(&now);
06027    if (!ast_strlen_zero(id))
06028       snprintf(idText, 256, "ActionID: %s\r\n", id);
06029    queue_iter = ao2_iterator_init(queues, 0);
06030    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
06031       ao2_lock(q);
06032 
06033       /* List queue properties */
06034       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06035          /* Reset the necessary local variables if no queuefilter is set*/
06036          qmemcount = 0;
06037          qmemavail = 0;
06038          qchancount = 0;
06039          qlongestholdtime = 0;
06040 
06041          /* List Queue Members */
06042          mem_iter = ao2_iterator_init(q->members, 0);
06043          while ((mem = ao2_iterator_next(&mem_iter))) {
06044             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
06045                ++qmemcount;
06046                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
06047                   ++qmemavail;
06048                }
06049             }
06050             ao2_ref(mem, -1);
06051          }
06052          ao2_iterator_destroy(&mem_iter);
06053          for (qe = q->head; qe; qe = qe->next) {
06054             if ((now - qe->start) > qlongestholdtime) {
06055                qlongestholdtime = now - qe->start;
06056             }
06057             ++qchancount;
06058          }
06059          astman_append(s, "Event: QueueSummary\r\n"
06060             "Queue: %s\r\n"
06061             "LoggedIn: %d\r\n"
06062             "Available: %d\r\n"
06063             "Callers: %d\r\n" 
06064             "HoldTime: %d\r\n"
06065             "LongestHoldTime: %d\r\n"
06066             "%s"
06067             "\r\n",
06068             q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
06069       }
06070       ao2_unlock(q);
06071       queue_t_unref(q, "Done with iterator");
06072    }
06073    ao2_iterator_destroy(&queue_iter);
06074    astman_append(s,
06075       "Event: QueueSummaryComplete\r\n"
06076       "%s"
06077       "\r\n", idText);
06078 
06079    return RESULT_SUCCESS;
06080 }

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

Definition at line 6229 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, RES_OUTOFMEMORY, and s.

Referenced by load_module().

06230 {
06231    const char *queuename, *interface;
06232 
06233    queuename = astman_get_header(m, "Queue");
06234    interface = astman_get_header(m, "Interface");
06235 
06236    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06237       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06238       return 0;
06239    }
06240 
06241    switch (remove_from_queue(queuename, interface)) {
06242    case RES_OKAY:
06243       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06244       astman_send_ack(s, m, "Removed interface from queue");
06245       break;
06246    case RES_EXISTS:
06247       astman_send_error(s, m, "Unable to remove interface: Not there");
06248       break;
06249    case RES_NOSUCHQUEUE:
06250       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06251       break;
06252    case RES_OUTOFMEMORY:
06253       astman_send_error(s, m, "Out of memory");
06254       break;
06255    case RES_NOT_DYNAMIC:
06256       astman_send_error(s, m, "Member not dynamic");
06257       break;
06258    }
06259 
06260    return 0;
06261 }

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

Definition at line 894 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

00895 {
00896    struct member *mem1 = obj1, *mem2 = obj2;
00897    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00898 }

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

Definition at line 882 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00883 {
00884    const struct member *mem = obj;
00885    const char *chname = strchr(mem->interface, '/');
00886    int ret = 0, i;
00887    if (!chname)
00888       chname = mem->interface;
00889    for (i = 0; i < 5 && chname[i]; i++)
00890       ret += compress_char(chname[i]) << (i * 6);
00891    return ret;
00892 }

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

02116 {
02117    struct member *mem;
02118    int avl = 0;
02119    struct ao2_iterator mem_iter;
02120 
02121    mem_iter = ao2_iterator_init(q->members, 0);
02122    while ((mem = ao2_iterator_next(&mem_iter))) {
02123       switch (mem->status) {
02124       case AST_DEVICE_INUSE:
02125          if (!q->ringinuse)
02126             break;
02127          /* else fall through */
02128       case AST_DEVICE_NOT_INUSE:
02129       case AST_DEVICE_UNKNOWN:
02130          if (!mem->paused) {
02131             avl++;
02132          }
02133          break;
02134       }
02135       ao2_ref(mem, -1);
02136 
02137       /* If autofill is not enabled or if the queue's strategy is ringall, then
02138        * we really don't care about the number of available members so much as we
02139        * do that there is at least one available.
02140        *
02141        * In fact, we purposely will return from this function stating that only
02142        * one member is available if either of those conditions hold. That way,
02143        * functions which determine what action to take based on the number of available
02144        * members will operate properly. The reasoning is that even if multiple
02145        * members are available, only the head caller can actually be serviced.
02146        */
02147       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02148          break;
02149       }
02150    }
02151    ao2_iterator_destroy(&mem_iter);
02152 
02153    return avl;
02154 }

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

Definition at line 1814 of file app_queue.c.

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

Referenced by say_periodic_announcement(), and say_position().

01815 {
01816    int res;
01817 
01818    if (ast_strlen_zero(filename)) {
01819       return 0;
01820    }
01821 
01822    ast_stopstream(chan);
01823 
01824    res = ast_streamfile(chan, filename, chan->language);
01825    if (!res)
01826       res = ast_waitstream(chan, AST_DIGIT_ANY);
01827 
01828    ast_stopstream(chan);
01829 
01830    return res;
01831 }

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

PauseQueueMember application.

Definition at line 4565 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

04566 {
04567    char *parse;
04568    AST_DECLARE_APP_ARGS(args,
04569       AST_APP_ARG(queuename);
04570       AST_APP_ARG(interface);
04571       AST_APP_ARG(options);
04572       AST_APP_ARG(reason);
04573    );
04574 
04575    if (ast_strlen_zero(data)) {
04576       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04577       return -1;
04578    }
04579 
04580    parse = ast_strdupa(data);
04581 
04582    AST_STANDARD_APP_ARGS(args, parse);
04583 
04584    if (ast_strlen_zero(args.interface)) {
04585       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04586       return -1;
04587    }
04588 
04589    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04590       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04591       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04592       return 0;
04593    }
04594 
04595    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04596 
04597    return 0;
04598 }

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

QueueLog application.

Definition at line 4755 of file app_queue.c.

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

04756 {
04757    char *parse;
04758 
04759    AST_DECLARE_APP_ARGS(args,
04760       AST_APP_ARG(queuename);
04761       AST_APP_ARG(uniqueid);
04762       AST_APP_ARG(membername);
04763       AST_APP_ARG(event);
04764       AST_APP_ARG(params);
04765    );
04766 
04767    if (ast_strlen_zero(data)) {
04768       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04769       return -1;
04770    }
04771 
04772    parse = ast_strdupa(data);
04773 
04774    AST_STANDARD_APP_ARGS(args, parse);
04775 
04776    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04777        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04778       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04779       return -1;
04780    }
04781 
04782    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04783       "%s", args.params ? args.params : "");
04784 
04785    return 0;
04786 }

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

Definition at line 599 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

00600 {
00601    struct call_queue *q = obj, *q2 = arg;
00602    return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0;
00603 }

static int queue_exec ( struct ast_channel chan,
void *  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 4833 of file app_queue.c.

References call_queue::announcefrequency, AST_APP_ARG, ast_cdr_noanswer(), ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_moh_start(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::cdr, queue_ent::chan, chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::expire, get_member_status(), 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, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, queue_ent::prio, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, status, stop, penalty_rule::time, try_calling(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04834 {
04835    int res=-1;
04836    int ringing=0;
04837    const char *user_priority;
04838    const char *max_penalty_str;
04839    const char *min_penalty_str;
04840    int prio;
04841    int qcontinue = 0;
04842    int max_penalty, min_penalty;
04843    enum queue_result reason = QUEUE_UNKNOWN;
04844    /* whether to exit Queue application after the timeout hits */
04845    int tries = 0;
04846    int noption = 0;
04847    char *parse;
04848    int makeannouncement = 0;
04849    AST_DECLARE_APP_ARGS(args,
04850       AST_APP_ARG(queuename);
04851       AST_APP_ARG(options);
04852       AST_APP_ARG(url);
04853       AST_APP_ARG(announceoverride);
04854       AST_APP_ARG(queuetimeoutstr);
04855       AST_APP_ARG(agi);
04856       AST_APP_ARG(macro);
04857       AST_APP_ARG(gosub);
04858       AST_APP_ARG(rule);
04859    );
04860    /* Our queue entry */
04861    struct queue_ent qe = { 0 };
04862    
04863    if (ast_strlen_zero(data)) {
04864       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n");
04865       return -1;
04866    }
04867    
04868    parse = ast_strdupa(data);
04869    AST_STANDARD_APP_ARGS(args, parse);
04870 
04871    /* Setup our queue entry */
04872    qe.start = time(NULL);
04873 
04874    /* set the expire time based on the supplied timeout; */
04875    if (!ast_strlen_zero(args.queuetimeoutstr))
04876       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04877    else
04878       qe.expire = 0;
04879 
04880    /* Get the priority from the variable ${QUEUE_PRIO} */
04881    ast_channel_lock(chan);
04882    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04883    if (user_priority) {
04884       if (sscanf(user_priority, "%30d", &prio) == 1) {
04885          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04886       } else {
04887          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04888             user_priority, chan->name);
04889          prio = 0;
04890       }
04891    } else {
04892       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04893       prio = 0;
04894    }
04895 
04896    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04897 
04898    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04899       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04900          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04901       } else {
04902          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04903             max_penalty_str, chan->name);
04904          max_penalty = 0;
04905       }
04906    } else {
04907       max_penalty = 0;
04908    }
04909 
04910    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04911       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
04912          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04913       } else {
04914          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04915             min_penalty_str, chan->name);
04916          min_penalty = 0;
04917       }
04918    } else {
04919       min_penalty = 0;
04920    }
04921    ast_channel_unlock(chan);
04922 
04923    if (args.options && (strchr(args.options, 'r')))
04924       ringing = 1;
04925 
04926    if (args.options && (strchr(args.options, 'c')))
04927       qcontinue = 1;
04928 
04929    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04930       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04931 
04932    qe.chan = chan;
04933    qe.prio = prio;
04934    qe.max_penalty = max_penalty;
04935    qe.min_penalty = min_penalty;
04936    qe.last_pos_said = 0;
04937    qe.last_pos = 0;
04938    qe.last_periodic_announce_time = time(NULL);
04939    qe.last_periodic_announce_sound = 0;
04940    qe.valid_digits = 0;
04941    if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04942       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04943       set_queue_result(chan, reason);
04944       return 0;
04945    }
04946    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04947       S_OR(chan->cid.cid_num, ""));
04948 check_turns:
04949    if (ringing) {
04950       ast_indicate(chan, AST_CONTROL_RINGING);
04951    } else {
04952       ast_moh_start(chan, qe.moh, NULL);
04953    }
04954 
04955    /* This is the wait loop for callers 2 through maxlen */
04956    res = wait_our_turn(&qe, ringing, &reason);
04957    if (res) {
04958       goto stop;
04959    }
04960 
04961    makeannouncement = 0;
04962 
04963    for (;;) {
04964       /* This is the wait loop for the head caller*/
04965       /* To exit, they may get their call answered; */
04966       /* they may dial a digit from the queue context; */
04967       /* or, they may timeout. */
04968 
04969       enum queue_member_status status = QUEUE_NORMAL;
04970       int exit = 0;
04971 
04972       /* Leave if we have exceeded our queuetimeout */
04973       if (qe.expire && (time(NULL) >= qe.expire)) {
04974          record_abandoned(&qe);
04975          ast_cdr_noanswer(qe.chan->cdr);
04976          reason = QUEUE_TIMEOUT;
04977          res = 0;
04978          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
04979             qe.pos, qe.opos, (long) time(NULL) - qe.start);
04980          break;
04981       }
04982 
04983       if (makeannouncement) {
04984          /* Make a position announcement, if enabled */
04985          if (qe.parent->announcefrequency)
04986             if ((res = say_position(&qe,ringing)))
04987                goto stop;
04988       }
04989       makeannouncement = 1;
04990 
04991       /* Make a periodic announcement, if enabled */
04992       if (qe.parent->periodicannouncefrequency)
04993          if ((res = say_periodic_announcement(&qe,ringing)))
04994             goto stop;
04995    
04996       /* Leave if we have exceeded our queuetimeout */
04997       if (qe.expire && (time(NULL) >= qe.expire)) {
04998          record_abandoned(&qe);
04999          ast_cdr_noanswer(qe.chan->cdr);
05000          reason = QUEUE_TIMEOUT;
05001          res = 0;
05002          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05003          break;
05004       }
05005 
05006       /* see if we need to move to the next penalty level for this queue */
05007       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
05008          update_qe_rule(&qe);
05009       }
05010 
05011       /* Try calling all queue members for 'timeout' seconds */
05012       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
05013       if (res) {
05014          goto stop;
05015       }
05016 
05017       /* exit after 'timeout' cycle if 'n' option enabled */
05018       if (noption && tries >= qe.parent->membercount) {
05019          ast_verb(3, "Exiting on time-out cycle\n");
05020          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
05021          record_abandoned(&qe);
05022          ast_cdr_noanswer(qe.chan->cdr);
05023          reason = QUEUE_TIMEOUT;
05024          res = 0;
05025          break;
05026       }
05027 
05028       for (; !exit || qe.pr; update_qe_rule(&qe)) {
05029          status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
05030 
05031          if (!qe.pr || status == QUEUE_NORMAL) {
05032             break;
05033          }
05034 
05035          if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
05036                ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
05037                ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
05038             continue;
05039          } else {
05040             exit = 1;
05041          }
05042       }
05043 
05044       /* leave the queue if no agents, if enabled */
05045       if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
05046          record_abandoned(&qe);
05047          ast_cdr_noanswer(qe.chan->cdr);
05048          reason = QUEUE_LEAVEEMPTY;
05049          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05050          res = 0;
05051          break;
05052       }
05053 
05054       /* leave the queue if no reachable agents, if enabled */
05055       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
05056          record_abandoned(&qe);
05057          reason = QUEUE_LEAVEUNAVAIL;
05058          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
05059          res = 0;
05060          break;
05061       }
05062       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
05063          record_abandoned(&qe);
05064          reason = QUEUE_LEAVEUNAVAIL;
05065          res = 0;
05066          break;
05067       }
05068 
05069       /* Leave if we have exceeded our queuetimeout */
05070       if (qe.expire && (time(NULL) >= qe.expire)) {
05071          record_abandoned(&qe);
05072          reason = QUEUE_TIMEOUT;
05073          res = 0;
05074          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
05075          break;
05076       }
05077 
05078       /* If using dynamic realtime members, we should regenerate the member list for this queue */
05079       update_realtime_members(qe.parent);
05080       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
05081       res = wait_a_bit(&qe);
05082       if (res)
05083          goto stop;
05084 
05085       /* Since this is a priority queue and
05086        * it is not sure that we are still at the head
05087        * of the queue, go and check for our turn again.
05088        */
05089       if (!is_our_turn(&qe)) {
05090          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
05091          goto check_turns;
05092       }
05093    }
05094 
05095 stop:
05096    if (res) {
05097       if (res < 0) {
05098          if (!qe.handled) {
05099             record_abandoned(&qe);
05100             ast_cdr_noanswer(qe.chan->cdr);
05101             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
05102                "%d|%d|%ld", qe.pos, qe.opos,
05103                (long) time(NULL) - qe.start);
05104             res = -1;
05105          } else if (qcontinue) {
05106             reason = QUEUE_CONTINUE;
05107             res = 0;
05108          }
05109       } else if (qe.valid_digits) {
05110          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05111             "%s|%d", qe.digits, qe.pos);
05112       }
05113    }
05114 
05115    /* Don't allow return code > 0 */
05116    if (res >= 0) {
05117       res = 0; 
05118       if (ringing) {
05119          ast_indicate(chan, -1);
05120       } else {
05121          ast_moh_stop(chan);
05122       }        
05123       ast_stopstream(chan);
05124    }
05125 
05126    set_queue_variables(qe.parent, qe.chan);
05127 
05128    leave_queue(&qe);
05129    if (reason != QUEUE_UNKNOWN)
05130       set_queue_result(chan, reason);
05131 
05132    if (qe.parent) {
05133       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
05134        * This ref must be taken away right before the queue_ent is destroyed.  In this case
05135        * the queue_ent is about to be returned on the stack */
05136       qe.parent = queue_unref(qe.parent);
05137    }
05138 
05139    return res;
05140 }

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

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

05377 {
05378    int penalty;
05379    AST_DECLARE_APP_ARGS(args,
05380       AST_APP_ARG(queuename);
05381       AST_APP_ARG(interface);
05382    );
05383    /* Make sure the returned value on error is NULL. */
05384    buf[0] = '\0';
05385 
05386    if (ast_strlen_zero(data)) {
05387       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05388       return -1;
05389    }
05390 
05391    AST_STANDARD_APP_ARGS(args, data);
05392 
05393    if (args.argc < 2) {
05394       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05395       return -1;
05396    }
05397 
05398    penalty = get_member_penalty (args.queuename, args.interface);
05399    
05400    if (penalty >= 0) /* remember that buf is already '\0' */
05401       snprintf (buf, len, "%d", penalty);
05402 
05403    return 0;
05404 }

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

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

05408 {
05409    int penalty;
05410    AST_DECLARE_APP_ARGS(args,
05411       AST_APP_ARG(queuename);
05412       AST_APP_ARG(interface);
05413    );
05414 
05415    if (ast_strlen_zero(data)) {
05416       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05417       return -1;
05418    }
05419 
05420    AST_STANDARD_APP_ARGS(args, data);
05421 
05422    if (args.argc < 2) {
05423       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05424       return -1;
05425    }
05426 
05427    penalty = atoi(value);
05428 
05429    if (ast_strlen_zero(args.interface)) {
05430       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05431       return -1;
05432    }
05433 
05434    /* if queuename = NULL then penalty will be set for interface in all the queues. */
05435    if (set_member_penalty(args.queuename, args.interface, penalty)) {
05436       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05437       return -1;
05438    }
05439 
05440    return 0;
05441 }

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 or total members of a specific queue.

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

Definition at line 5195 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, load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_t_unref, and member::status.

05196 {
05197    int count = 0;
05198    struct member *m;
05199    struct ao2_iterator mem_iter;
05200    struct call_queue *q;
05201    char *option;
05202 
05203    if (ast_strlen_zero(data)) {
05204       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05205       return -1;
05206    }
05207 
05208    if ((option = strchr(data, ',')))
05209       *option++ = '\0';
05210    else
05211       option = "logged";
05212    if ((q = load_realtime_queue(data))) {
05213       ao2_lock(q);
05214       if (!strcasecmp(option, "logged")) {
05215          mem_iter = ao2_iterator_init(q->members, 0);
05216          while ((m = ao2_iterator_next(&mem_iter))) {
05217             /* Count the agents who are logged in and presently answering calls */
05218             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05219                count++;
05220             }
05221             ao2_ref(m, -1);
05222          }
05223          ao2_iterator_destroy(&mem_iter);
05224       } else if (!strcasecmp(option, "free")) {
05225          mem_iter = ao2_iterator_init(q->members, 0);
05226          while ((m = ao2_iterator_next(&mem_iter))) {
05227             /* Count the agents who are logged in and presently answering calls */
05228             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05229                count++;
05230             }
05231             ao2_ref(m, -1);
05232          }
05233          ao2_iterator_destroy(&mem_iter);
05234       } else /* must be "count" */
05235          count = q->membercount;
05236       ao2_unlock(q);
05237       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()");
05238    } else
05239       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05240 
05241    snprintf(buf, len, "%d", count);
05242 
05243    return 0;
05244 }

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

05252 {
05253    int count = 0;
05254    struct member *m;
05255    struct call_queue *q;
05256    struct ao2_iterator mem_iter;
05257    static int depflag = 1;
05258 
05259    if (depflag) {
05260       depflag = 0;
05261       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");
05262    }
05263 
05264    if (ast_strlen_zero(data)) {
05265       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05266       return -1;
05267    }
05268    
05269    if ((q = load_realtime_queue(data))) {
05270       ao2_lock(q);
05271       mem_iter = ao2_iterator_init(q->members, 0);
05272       while ((m = ao2_iterator_next(&mem_iter))) {
05273          /* Count the agents who are logged in and presently answering calls */
05274          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05275             count++;
05276          }
05277          ao2_ref(m, -1);
05278       }
05279       ao2_iterator_destroy(&mem_iter);
05280       ao2_unlock(q);
05281       queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT");
05282    } else
05283       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05284 
05285    snprintf(buf, len, "%d", count);
05286 
05287    return 0;
05288 }

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

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

05328 {
05329    struct call_queue *q, tmpq = {
05330       .name = data,  
05331    };
05332    struct member *m;
05333 
05334    /* Ensure an otherwise empty list doesn't return garbage */
05335    buf[0] = '\0';
05336 
05337    if (ast_strlen_zero(data)) {
05338       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05339       return -1;
05340    }
05341 
05342    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) {
05343       int buflen = 0, count = 0;
05344       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05345 
05346       ao2_lock(q);
05347       while ((m = ao2_iterator_next(&mem_iter))) {
05348          /* strcat() is always faster than printf() */
05349          if (count++) {
05350             strncat(buf + buflen, ",", len - buflen - 1);
05351             buflen++;
05352          }
05353          strncat(buf + buflen, m->interface, len - buflen - 1);
05354          buflen += strlen(m->interface);
05355          /* Safeguard against overflow (negative length) */
05356          if (buflen >= len - 2) {
05357             ao2_ref(m, -1);
05358             ast_log(LOG_WARNING, "Truncating list\n");
05359             break;
05360          }
05361          ao2_ref(m, -1);
05362       }
05363       ao2_iterator_destroy(&mem_iter);
05364       ao2_unlock(q);
05365       queue_t_unref(q, "Done with find for QUEUE_MEMBER_LIST()");
05366    } else
05367       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05368 
05369    /* We should already be terminated, but let's make sure. */
05370    buf[len - 1] = '\0';
05371 
05372    return 0;
05373 }

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

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

05292 {
05293    int count = 0;
05294    struct call_queue *q, tmpq = {
05295       .name = data,  
05296    };
05297    struct ast_variable *var = NULL;
05298 
05299    buf[0] = '\0';
05300    
05301    if (ast_strlen_zero(data)) {
05302       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05303       return -1;
05304    }
05305 
05306    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) {
05307       ao2_lock(q);
05308       count = q->count;
05309       ao2_unlock(q);
05310       queue_t_unref(q, "Done with find for QUEUE_WAITING_COUNT");
05311    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05312       /* if the queue is realtime but was not found in memory, this
05313        * means that the queue had been deleted from memory since it was 
05314        * "dead." This means it has a 0 waiting count
05315        */
05316       count = 0;
05317       ast_variables_destroy(var);
05318    } else
05319       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05320 
05321    snprintf(buf, len, "%d", count);
05322 
05323    return 0;
05324 }

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 5147 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, chan, call_queue::count, call_queue::holdtime, int2strat(), LOG_ERROR, LOG_WARNING, call_queue::maxlen, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_t_unref, queues, call_queue::servicelevel, call_queue::setqueuevar, and call_queue::strategy.

05148 {
05149    int res = -1;
05150    struct call_queue *q, tmpq = {
05151       .name = data,  
05152    };
05153 
05154    char interfacevar[256] = "";
05155    float sl = 0;
05156 
05157    if (ast_strlen_zero(data)) {
05158       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05159       return -1;
05160    }
05161 
05162    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) {
05163       ao2_lock(q);
05164       if (q->setqueuevar) {
05165          sl = 0;
05166          res = 0;
05167 
05168          if (q->callscompleted > 0) {
05169             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05170          }
05171 
05172          snprintf(interfacevar, sizeof(interfacevar),
05173             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05174             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05175 
05176          pbx_builtin_setvar_multiple(chan, interfacevar);
05177       }
05178 
05179       ao2_unlock(q);
05180       queue_t_unref(q, "Done with temporary reference in QUEUE() function");
05181    } else {
05182       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05183    }
05184 
05185    snprintf(buf, len, "%d", res);
05186 
05187    return 0;
05188 }

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

Definition at line 592 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

00593 {
00594    const struct call_queue *q = obj;
00595 
00596    return ast_str_case_hash(q->name);
00597 }

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

Definition at line 617 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

00618 {
00619    ao2_ref(q, 1);
00620    return q;
00621 }

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 1157 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, buf, 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, call_queue::periodicannouncefrequency, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, s, 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_queues().

01158 {
01159    if (!strcasecmp(param, "musicclass") || 
01160       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01161       ast_string_field_set(q, moh, val);
01162    } else if (!strcasecmp(param, "announce")) {
01163       ast_string_field_set(q, announce, val);
01164    } else if (!strcasecmp(param, "context")) {
01165       ast_string_field_set(q, context, val);
01166    } else if (!strcasecmp(param, "timeout")) {
01167       q->timeout = atoi(val);
01168       if (q->timeout < 0)
01169          q->timeout = DEFAULT_TIMEOUT;
01170    } else if (!strcasecmp(param, "ringinuse")) {
01171       q->ringinuse = ast_true(val);
01172    } else if (!strcasecmp(param, "setinterfacevar")) {
01173       q->setinterfacevar = ast_true(val);
01174    } else if (!strcasecmp(param, "setqueuevar")) {
01175       q->setqueuevar = ast_true(val);
01176    } else if (!strcasecmp(param, "setqueueentryvar")) {
01177       q->setqueueentryvar = ast_true(val);
01178    } else if (!strcasecmp(param, "monitor-format")) {
01179       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01180    } else if (!strcasecmp(param, "membermacro")) {
01181       ast_string_field_set(q, membermacro, val);
01182    } else if (!strcasecmp(param, "membergosub")) {
01183       ast_string_field_set(q, membergosub, val);
01184    } else if (!strcasecmp(param, "queue-youarenext")) {
01185       ast_string_field_set(q, sound_next, val);
01186    } else if (!strcasecmp(param, "queue-thereare")) {
01187       ast_string_field_set(q, sound_thereare, val);
01188    } else if (!strcasecmp(param, "queue-callswaiting")) {
01189       ast_string_field_set(q, sound_calls, val);
01190    } else if (!strcasecmp(param, "queue-quantity1")) {
01191       ast_string_field_set(q, queue_quantity1, val);
01192    } else if (!strcasecmp(param, "queue-quantity2")) {
01193       ast_string_field_set(q, queue_quantity2, val);
01194    } else if (!strcasecmp(param, "queue-holdtime")) {
01195       ast_string_field_set(q, sound_holdtime, val);
01196    } else if (!strcasecmp(param, "queue-minutes")) {
01197       ast_string_field_set(q, sound_minutes, val);
01198    } else if (!strcasecmp(param, "queue-minute")) {
01199       ast_string_field_set(q, sound_minute, val);
01200    } else if (!strcasecmp(param, "queue-seconds")) {
01201       ast_string_field_set(q, sound_seconds, val);
01202    } else if (!strcasecmp(param, "queue-thankyou")) {
01203       ast_string_field_set(q, sound_thanks, val);
01204    } else if (!strcasecmp(param, "queue-callerannounce")) {
01205       ast_string_field_set(q, sound_callerannounce, val);
01206    } else if (!strcasecmp(param, "queue-reporthold")) {
01207       ast_string_field_set(q, sound_reporthold, val);
01208    } else if (!strcasecmp(param, "announce-frequency")) {
01209       q->announcefrequency = atoi(val);
01210    } else if (!strcasecmp(param, "min-announce-frequency")) {
01211       q->minannouncefrequency = atoi(val);
01212       ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name);
01213    } else if (!strcasecmp(param, "announce-round-seconds")) {
01214       q->roundingseconds = atoi(val);
01215       /* Rounding to any other values just doesn't make sense... */
01216       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01217          || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01218          if (linenum >= 0) {
01219             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01220                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01221                val, param, q->name, linenum);
01222          } else {
01223             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01224                "using 0 instead for queue '%s'\n", val, param, q->name);
01225          }
01226          q->roundingseconds=0;
01227       }
01228    } else if (!strcasecmp(param, "announce-holdtime")) {
01229       if (!strcasecmp(val, "once"))
01230          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01231       else if (ast_true(val))
01232          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01233       else
01234          q->announceholdtime = 0;
01235    } else if (!strcasecmp(param, "announce-position")) {
01236       if (!strcasecmp(val, "limit"))
01237          q->announceposition = ANNOUNCEPOSITION_LIMIT;
01238       else if (!strcasecmp(val, "more"))
01239          q->announceposition = ANNOUNCEPOSITION_MORE_THAN;
01240       else if (ast_true(val))
01241          q->announceposition = ANNOUNCEPOSITION_YES;
01242       else
01243          q->announceposition = ANNOUNCEPOSITION_NO;
01244    } else if (!strcasecmp(param, "announce-position-limit")) {
01245       q->announcepositionlimit = atoi(val);
01246    } else if (!strcasecmp(param, "periodic-announce")) {
01247       if (strchr(val, ',')) {
01248          char *s, *buf = ast_strdupa(val);
01249          unsigned int i = 0;
01250 
01251          while ((s = strsep(&buf, ",|"))) {
01252             if (!q->sound_periodicannounce[i])
01253                q->sound_periodicannounce[i] = ast_str_create(16);
01254             ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s);
01255             i++;
01256             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01257                break;
01258          }
01259          q->numperiodicannounce = i;
01260       } else {
01261          ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val);
01262          q->numperiodicannounce = 1;
01263       }
01264    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01265       q->periodicannouncefrequency = atoi(val);
01266    } else if (!strcasecmp(param, "random-periodic-announce")) {
01267       q->randomperiodicannounce = ast_true(val);
01268    } else if (!strcasecmp(param, "retry")) {
01269       q->retry = atoi(val);
01270       if (q->retry <= 0)
01271          q->retry = DEFAULT_RETRY;
01272    } else if (!strcasecmp(param, "wrapuptime")) {
01273       q->wrapuptime = atoi(val);
01274    } else if (!strcasecmp(param, "autofill")) {
01275       q->autofill = ast_true(val);
01276    } else if (!strcasecmp(param, "monitor-type")) {
01277       if (!strcasecmp(val, "mixmonitor"))
01278          q->montype = 1;
01279    } else if (!strcasecmp(param, "autopause")) {
01280       q->autopause = ast_true(val);
01281    } else if (!strcasecmp(param, "maxlen")) {
01282       q->maxlen = atoi(val);
01283       if (q->maxlen < 0)
01284          q->maxlen = 0;
01285    } else if (!strcasecmp(param, "servicelevel")) {
01286       q->servicelevel= atoi(val);
01287    } else if (!strcasecmp(param, "strategy")) {
01288       int strategy;
01289 
01290       /* We are a static queue and already have set this, no need to do it again */
01291       if (failunknown) {
01292          return;
01293       }
01294       strategy = strat2int(val);
01295       if (strategy < 0) {
01296          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01297             val, q->name);
01298          q->strategy = QUEUE_STRATEGY_RINGALL;
01299       }
01300       if (strategy == q->strategy) {
01301          return;
01302       }
01303       if (strategy == QUEUE_STRATEGY_LINEAR) {
01304          ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n");
01305          return;
01306       }
01307       q->strategy = strategy;
01308    } else if (!strcasecmp(param, "joinempty")) {
01309       if (!strcasecmp(val, "loose"))
01310          q->joinempty = QUEUE_EMPTY_LOOSE;
01311       else if (!strcasecmp(val, "strict"))
01312          q->joinempty = QUEUE_EMPTY_STRICT;
01313       else if (ast_true(val))
01314          q->joinempty = QUEUE_EMPTY_NORMAL;
01315       else
01316          q->joinempty = 0;
01317    } else if (!strcasecmp(param, "leavewhenempty")) {
01318       if (!strcasecmp(val, "loose"))
01319          q->leavewhenempty = QUEUE_EMPTY_LOOSE;
01320       else if (!strcasecmp(val, "strict"))
01321          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01322       else if (ast_true(val))
01323          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01324       else
01325          q->leavewhenempty = 0;
01326    } else if (!strcasecmp(param, "eventmemberstatus")) {
01327       q->maskmemberstatus = !ast_true(val);
01328    } else if (!strcasecmp(param, "eventwhencalled")) {
01329       if (!strcasecmp(val, "vars")) {
01330          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01331       } else {
01332          q->eventwhencalled = ast_true(val) ? 1 : 0;
01333       }
01334    } else if (!strcasecmp(param, "reportholdtime")) {
01335       q->reportholdtime = ast_true(val);
01336    } else if (!strcasecmp(param, "memberdelay")) {
01337       q->memberdelay = atoi(val);
01338    } else if (!strcasecmp(param, "weight")) {
01339       q->weight = atoi(val);
01340       if (q->weight)
01341          use_weight++;
01342       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01343          we will not see any effect on use_weight until next reload. */
01344    } else if (!strcasecmp(param, "timeoutrestart")) {
01345       q->timeoutrestart = ast_true(val);
01346    } else if (!strcasecmp(param, "defaultrule")) {
01347       ast_string_field_set(q, defaultrule, val);
01348    } else if (!strcasecmp(param, "timeoutpriority")) {
01349       if (!strcasecmp(val, "conf")) {
01350          q->timeoutpriority = TIMEOUT_PRIORITY_CONF;
01351       } else {
01352          q->timeoutpriority = TIMEOUT_PRIORITY_APP;
01353       }
01354    } else if (failunknown) {
01355       if (linenum >= 0) {
01356          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01357             q->name, param, linenum);
01358       } else {
01359          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01360       }
01361    }
01362 }

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

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

05956 {
05957    switch ( cmd ) {
05958    case CLI_INIT:
05959       e->command = "queue show";
05960       e->usage =
05961          "Usage: queue show\n"
05962          "       Provides summary information on a specified queue.\n";
05963       return NULL;
05964    case CLI_GENERATE:
05965       return complete_queue_show(a->line, a->word, a->pos, a->n); 
05966    }
05967 
05968    return __queues_show(NULL, a->fd, a->argc, a->argv);
05969 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3235 of file app_queue.c.

References ast_free.

03236 {
03237    struct queue_transfer_ds *qtds = data;
03238    ast_free(qtds);
03239 }

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

03259 {
03260    struct queue_transfer_ds *qtds = data;
03261    struct queue_ent *qe = qtds->qe;
03262    struct member *member = qtds->member;
03263    time_t callstart = qtds->starttime;
03264    int callcompletedinsl = qtds->callcompletedinsl;
03265    struct ast_datastore *datastore;
03266 
03267    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03268             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03269             (long) (time(NULL) - callstart), qe->opos);
03270 
03271    update_queue(qe->parent, member, callcompletedinsl);
03272    
03273    /* No need to lock the channels because they are already locked in ast_do_masquerade */
03274    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03275       ast_channel_datastore_remove(old_chan, datastore);
03276    } else {
03277       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03278    }
03279 }

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

Definition at line 623 of file app_queue.c.

References ao2_ref.

00624 {
00625    ao2_ref(q, -1);
00626    return q;
00627 }

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

Definition at line 2015 of file app_queue.c.

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

02016 {
02017    int oldvalue;
02018 
02019    /* Calculate holdtime using an exponential average */
02020    /* Thanks to SRT for this contribution */
02021    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
02022 
02023    ao2_lock(qe->parent);
02024    oldvalue = qe->parent->holdtime;
02025    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
02026    ao2_unlock(qe->parent);
02027 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

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

02571 {
02572    ao2_lock(qe->parent);
02573    set_queue_variables(qe->parent, qe->chan);
02574    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02575       "Queue: %s\r\n"
02576       "Uniqueid: %s\r\n"
02577       "Position: %d\r\n"
02578       "OriginalPosition: %d\r\n"
02579       "HoldTime: %d\r\n",
02580       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02581 
02582    qe->parent->callsabandoned++;
02583    ao2_unlock(qe->parent);
02584 }

static int reload ( void   )  [static]

Definition at line 6857 of file app_queue.c.

References ast_unload_realtime(), and reload_queues().

06858 {
06859    ast_unload_realtime("queue_members");
06860    reload_queues(1);
06861    return 0;
06862 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 4468 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, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_t_unref, queues, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

04469 {
04470    char *cur_ptr;
04471    const char *queue_name;
04472    char *member;
04473    char *interface;
04474    char *membername = NULL;
04475    char *state_interface;
04476    char *penalty_tok;
04477    int penalty = 0;
04478    char *paused_tok;
04479    int paused = 0;
04480    struct ast_db_entry *db_tree;
04481    struct ast_db_entry *entry;
04482    struct call_queue *cur_queue;
04483    char queue_data[PM_MAX_LEN];
04484 
04485    ao2_lock(queues);
04486 
04487    /* Each key in 'pm_family' is the name of a queue */
04488    db_tree = ast_db_gettree(pm_family, NULL);
04489    for (entry = db_tree; entry; entry = entry->next) {
04490 
04491       queue_name = entry->key + strlen(pm_family) + 2;
04492 
04493       {
04494          struct call_queue tmpq = {
04495             .name = queue_name,
04496          };
04497          cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members");
04498       }  
04499 
04500       if (!cur_queue)
04501          cur_queue = load_realtime_queue(queue_name);
04502 
04503       if (!cur_queue) {
04504          /* If the queue no longer exists, remove it from the
04505           * database */
04506          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04507          ast_db_del(pm_family, queue_name);
04508          continue;
04509       } 
04510 
04511       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04512          queue_t_unref(cur_queue, "Expire reload reference");
04513          continue;
04514       }
04515 
04516       cur_ptr = queue_data;
04517       while ((member = strsep(&cur_ptr, ",|"))) {
04518          if (ast_strlen_zero(member))
04519             continue;
04520 
04521          interface = strsep(&member, ";");
04522          penalty_tok = strsep(&member, ";");
04523          paused_tok = strsep(&member, ";");
04524          membername = strsep(&member, ";");
04525          state_interface = strsep(&member, ";");
04526 
04527          if (!penalty_tok) {
04528             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04529             break;
04530          }
04531          penalty = strtol(penalty_tok, NULL, 10);
04532          if (errno == ERANGE) {
04533             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04534             break;
04535          }
04536          
04537          if (!paused_tok) {
04538             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04539             break;
04540          }
04541          paused = strtol(paused_tok, NULL, 10);
04542          if ((errno == ERANGE) || paused < 0 || paused > 1) {
04543             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04544             break;
04545          }
04546 
04547          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
04548          
04549          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04550             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04551             break;
04552          }
04553       }
04554       queue_t_unref(cur_queue, "Expire reload reference");
04555    }
04556 
04557    ao2_unlock(queues);
04558    if (db_tree) {
04559       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04560       ast_db_freetree(db_tree);
04561    }
04562 }

static int reload_queue_rules ( int  reload  )  [static]

Definition at line 5512 of file app_queue.c.

References ast_calloc, ast_category_browse(), 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_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 handle_queue_rule_reload(), and reload_queues().

05513 {
05514    struct ast_config *cfg;
05515    struct rule_list *rl_iter, *new_rl;
05516    struct penalty_rule *pr_iter;
05517    char *rulecat = NULL;
05518    struct ast_variable *rulevar = NULL;
05519    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05520    
05521    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05522       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05523    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05524       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05525       return AST_MODULE_LOAD_SUCCESS;
05526    } else {
05527       AST_LIST_LOCK(&rule_lists);
05528       while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05529          while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05530             ast_free(pr_iter);
05531          ast_free(rl_iter);
05532       }
05533       while ((rulecat = ast_category_browse(cfg, rulecat))) {
05534          if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05535             ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05536             AST_LIST_UNLOCK(&rule_lists);
05537             return AST_MODULE_LOAD_FAILURE;
05538          } else {
05539             ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05540             AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05541             for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05542                if(!strcasecmp(rulevar->name, "penaltychange")) {
05543                   insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05544                } else {
05545                   ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05546                }
05547          }
05548       }
05549       AST_LIST_UNLOCK(&rule_lists);
05550    }
05551 
05552    ast_config_destroy(cfg);
05553 
05554    return AST_MODULE_LOAD_SUCCESS;
05555 }

static int reload_queues ( int  reload  )  [static]

Definition at line 5558 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find, AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_lock(), ao2_ref, ao2_t_find, ao2_t_iterator_next, ao2_unlink, ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_load, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, ast_log(), AST_MODULE_LOAD_FAILURE, AST_STANDARD_APP_ARGS, ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEUNCHANGED, create_queue_member(), call_queue::dead, member::delme, member::dynamic, call_queue::found, init_queue(), member::interface, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_link, call_queue::realtime, reload_queue_rules(), remove_from_interfaces(), member::state_interface, strat2int(), and var.

Referenced by load_module(), and reload().

05559 {
05560    struct call_queue *q;
05561    struct ast_config *cfg;
05562    char *cat, *tmp;
05563    struct ast_variable *var;
05564    struct member *cur, *newm;
05565    struct ao2_iterator mem_iter;
05566    int new;
05567    const char *general_val = NULL;
05568    char *parse;
05569    char *interface, *state_interface;
05570    char *membername = NULL;
05571    int penalty;
05572    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05573    struct ao2_iterator queue_iter;
05574    AST_DECLARE_APP_ARGS(args,
05575       AST_APP_ARG(interface);
05576       AST_APP_ARG(penalty);
05577       AST_APP_ARG(membername);
05578       AST_APP_ARG(state_interface);
05579    );
05580 
05581    /*First things first. Let's load queuerules.conf*/
05582    if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05583       return AST_MODULE_LOAD_FAILURE;
05584       
05585    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05586       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05587       return 0;
05588    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05589       return 0;
05590    ao2_lock(queues);
05591    use_weight=0;
05592    /* Mark all queues as dead for the moment */
05593    queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK);
05594    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05595       if (!q->realtime) {
05596          q->dead = 1;
05597          q->found = 0;
05598       }
05599       queue_t_unref(q, "Done with iterator");
05600    }
05601 
05602    /* Chug through config file */
05603    cat = NULL;
05604    while ((cat = ast_category_browse(cfg, cat)) ) {
05605       if (!strcasecmp(cat, "general")) {  
05606          /* Initialize global settings */
05607          queue_keep_stats = 0;
05608          if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05609             queue_keep_stats = ast_true(general_val);
05610          queue_persistent_members = 0;
05611          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05612             queue_persistent_members = ast_true(general_val);
05613          autofill_default = 0;
05614          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05615             autofill_default = ast_true(general_val);
05616          montype_default = 0;
05617          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05618             if (!strcasecmp(general_val, "mixmonitor"))
05619                montype_default = 1;
05620          }
05621          update_cdr = 0;
05622          if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05623             update_cdr = ast_true(general_val);
05624          shared_lastcall = 0;
05625          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05626             shared_lastcall = ast_true(general_val);
05627       } else { /* Define queue */
05628          /* Look for an existing one */
05629          struct call_queue tmpq = {
05630             .name = cat,
05631          };
05632          if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Verify whether we exist or not"))) {
05633             /* Make one then */
05634             if (!(q = alloc_queue(cat))) {
05635                /* TODO: Handle memory allocation failure */
05636             }
05637             new = 1;
05638          } else
05639             new = 0;
05640          if (q) {
05641             const char *tmpvar = NULL;
05642             if (!new)
05643                ao2_lock(q);
05644             /* Check if a queue with this name already exists */
05645             if (q->found) {
05646                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05647                if (!new) {
05648                   ao2_unlock(q);
05649                   queue_t_unref(q, "We exist! Expiring temporary pointer");
05650                }
05651                continue;
05652             }
05653             /* Due to the fact that the "linear" strategy will have a different allocation
05654              * scheme for queue members, we must devise the queue's strategy before other initializations
05655              */
05656             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05657                q->strategy = strat2int(tmpvar);
05658                if (q->strategy < 0) {
05659                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05660                   tmpvar, q->name);
05661                   q->strategy = QUEUE_STRATEGY_RINGALL;
05662                }
05663             } else
05664                q->strategy = QUEUE_STRATEGY_RINGALL;
05665             /* Re-initialize the queue, and clear statistics */
05666             init_queue(q);
05667             if (!queue_keep_stats) 
05668                clear_queue(q);
05669             mem_iter = ao2_iterator_init(q->members, 0);
05670             while ((cur = ao2_iterator_next(&mem_iter))) {
05671                if (!cur->dynamic) {
05672                   cur->delme = 1;
05673                }
05674                ao2_ref(cur, -1);
05675             }
05676             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05677                if (!strcasecmp(var->name, "member")) {
05678                   struct member tmpmem;
05679                   membername = NULL;
05680 
05681                   if (ast_strlen_zero(var->value)) {
05682                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05683                      continue;
05684                   }
05685 
05686                   /* Add a new member */
05687                   if (!(parse = ast_strdup(var->value))) {
05688                      continue;
05689                   }
05690                   
05691                   AST_STANDARD_APP_ARGS(args, parse);
05692 
05693                   interface = args.interface;
05694                   if (!ast_strlen_zero(args.penalty)) {
05695                      tmp = args.penalty;
05696                      while (*tmp && *tmp < 33) tmp++;
05697                      penalty = atoi(tmp);
05698                      if (penalty < 0) {
05699                         penalty = 0;
05700                      }
05701                   } else
05702                      penalty = 0;
05703 
05704                   if (!ast_strlen_zero(args.membername)) {
05705                      membername = args.membername;
05706                      while (*membername && *membername < 33) membername++;
05707                   }
05708 
05709                   if (!ast_strlen_zero(args.state_interface)) {
05710                      state_interface = args.state_interface;
05711                      while (*state_interface && *state_interface < 33) state_interface++;
05712                   } else
05713                      state_interface = interface;
05714 
05715                   /* Find the old position in the list */
05716                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05717                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05718                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
05719                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
05720                      remove_from_interfaces(cur->state_interface, 0);
05721                   }
05722                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05723                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05724                      add_to_interfaces(state_interface);
05725                   ao2_link(q->members, newm);
05726                   ao2_ref(newm, -1);
05727                   newm = NULL;
05728 
05729                   if (cur)
05730                      ao2_ref(cur, -1);
05731                   else {
05732                      q->membercount++;
05733                   }
05734                   ast_free(parse);
05735                } else {
05736                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05737                }
05738             }
05739 
05740             /* Free remaining members marked as delme */
05741             mem_iter = ao2_iterator_init(q->members, 0);
05742             while ((cur = ao2_iterator_next(&mem_iter))) {
05743                if (! cur->delme) {
05744                   ao2_ref(cur, -1);
05745                   continue;
05746                }
05747                q->membercount--;
05748                ao2_unlink(q->members, cur);
05749                remove_from_interfaces(cur->interface, 0);
05750                ao2_ref(cur, -1);
05751             }
05752 
05753             if (new) {
05754                queues_t_link(queues, q, "Add queue to container");
05755             } else 
05756                ao2_unlock(q);
05757             queue_t_unref(q, "Added queue to container, deleting creation pointer");
05758          }
05759       }
05760    }
05761    ast_config_destroy(cfg);
05762    queue_iter = ao2_iterator_init(queues, 0);
05763    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
05764       if (q->dead) {
05765          queues_t_unlink(queues, q, "Remove queue from container because marked as dead");
05766       } else {
05767          ao2_lock(q);
05768          mem_iter = ao2_iterator_init(q->members, 0);
05769          while ((cur = ao2_iterator_next(&mem_iter))) {
05770             if (cur->dynamic)
05771                q->membercount++;
05772             cur->status = ast_device_state(cur->state_interface);
05773             ao2_ref(cur, -1);
05774          }
05775          ao2_unlock(q);
05776       }
05777       queue_t_unref(q, "Done with iterator");
05778    }
05779    ao2_unlock(queues);
05780    return 1;
05781 }

static int remove_from_interfaces ( const char *  interface,
int  lock_queue_container 
) [static]

Definition at line 1037 of file app_queue.c.

References ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, member_interface::interface, interface_exists_global(), and member_interface::list.

Referenced by free_members(), reload_queues(), remove_from_queue(), rt_handle_member_record(), and update_realtime_members().

01038 {
01039    struct member_interface *curint;
01040 
01041    if (interface_exists_global(interface, lock_queue_container))
01042       return 0;
01043 
01044    AST_LIST_LOCK(&interfaces);
01045    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01046       if (!strcasecmp(curint->interface, interface)) {
01047          ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01048          AST_LIST_REMOVE_CURRENT(list);
01049          ast_free(curint);
01050          break;
01051       }
01052    }
01053    AST_LIST_TRAVERSE_SAFE_END;
01054    AST_LIST_UNLOCK(&interfaces);
01055 
01056    return 0;
01057 }

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 4202 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, queues, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::state_interface.

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

04203 {
04204    struct call_queue *q, tmpq = {
04205       .name = queuename,   
04206    };
04207    struct member *mem, tmpmem;
04208    int res = RES_NOSUCHQUEUE;
04209 
04210    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04211    if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) {
04212       ao2_lock(queues);
04213       ao2_lock(q);
04214       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04215          /* XXX future changes should beware of this assumption!! */
04216          if (!mem->dynamic) {
04217             ao2_ref(mem, -1);
04218             ao2_unlock(q);
04219             queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference");
04220             ao2_unlock(queues);
04221             return RES_NOT_DYNAMIC;
04222          }
04223          q->membercount--;
04224          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04225             "Queue: %s\r\n"
04226             "Location: %s\r\n"
04227             "MemberName: %s\r\n",
04228             q->name, mem->interface, mem->membername);
04229          ao2_unlink(q->members, mem);
04230          remove_from_interfaces(mem->state_interface, 0);
04231          ao2_ref(mem, -1);
04232 
04233          if (queue_persistent_members)
04234             dump_queue_members(q);
04235          
04236          res = RES_OKAY;
04237       } else {
04238          res = RES_EXISTS;
04239       }
04240       ao2_unlock(q);
04241       ao2_unlock(queues);
04242       queue_t_unref(q, "Expiring temporary reference");
04243    }
04244 
04245    return res;
04246 }

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

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, 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_unlock, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_free, ast_request(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, manager_event, callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, status, callattempt::stillgoing, ast_channel::uniqueid, update_status(), ast_cdr::userfield, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

02255 {
02256    int res;
02257    int status;
02258    char tech[256];
02259    char *location;
02260    const char *macrocontext, *macroexten;
02261 
02262    /* on entry here, we know that tmp->chan == NULL */
02263    if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
02264       (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
02265       ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 
02266             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
02267       if (qe->chan->cdr)
02268          ast_cdr_busy(qe->chan->cdr);
02269       tmp->stillgoing = 0;
02270       (*busies)++;
02271       return 0;
02272    }
02273 
02274    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
02275       ast_debug(1, "%s in use, can't receive call\n", tmp->interface);
02276       if (qe->chan->cdr)
02277          ast_cdr_busy(qe->chan->cdr);
02278       tmp->stillgoing = 0;
02279       return 0;
02280    }
02281 
02282    if (tmp->member->paused) {
02283       ast_debug(1, "%s paused, can't receive call\n", tmp->interface);
02284       if (qe->chan->cdr)
02285          ast_cdr_busy(qe->chan->cdr);
02286       tmp->stillgoing = 0;
02287       return 0;
02288    }
02289    if (use_weight && compare_weight(qe->parent,tmp->member)) {
02290       ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
02291       if (qe->chan->cdr)
02292          ast_cdr_busy(qe->chan->cdr);
02293       tmp->stillgoing = 0;
02294       (*busies)++;
02295       return 0;
02296    }
02297 
02298    ast_copy_string(tech, tmp->interface, sizeof(tech));
02299    if ((location = strchr(tech, '/')))
02300       *location++ = '\0';
02301    else
02302       location = "";
02303 
02304    /* Request the peer */
02305    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02306    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02307       if (qe->chan->cdr)
02308          ast_cdr_busy(qe->chan->cdr);
02309       tmp->stillgoing = 0;
02310 
02311       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02312 
02313       ao2_lock(qe->parent);
02314       qe->parent->rrpos++;
02315       qe->linpos++;
02316       ao2_unlock(qe->parent);
02317 
02318 
02319       (*busies)++;
02320       return 0;
02321    }
02322    
02323    tmp->chan->appl = "AppQueue";
02324    tmp->chan->data = "(Outgoing Line)";
02325    memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup));
02326    if (tmp->chan->cid.cid_num)
02327       ast_free(tmp->chan->cid.cid_num);
02328    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02329    if (tmp->chan->cid.cid_name)
02330       ast_free(tmp->chan->cid.cid_name);
02331    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02332    if (tmp->chan->cid.cid_ani)
02333       ast_free(tmp->chan->cid.cid_ani);
02334    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02335 
02336    /* Inherit specially named variables from parent channel */
02337    ast_channel_inherit_variables(qe->chan, tmp->chan);
02338    ast_channel_datastore_inherit(qe->chan, tmp->chan);
02339 
02340    /* Presense of ADSI CPE on outgoing channel follows ours */
02341    tmp->chan->adsicpe = qe->chan->adsicpe;
02342 
02343    /* Inherit context and extension */
02344    ast_channel_lock(qe->chan);
02345    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02346    ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext);
02347    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02348    if (!ast_strlen_zero(macroexten))
02349       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02350    else
02351       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02352    if (ast_cdr_isset_unanswered()) {
02353       /* they want to see the unanswered dial attempts! */
02354       /* set up the CDR fields on all the CDRs to give sensical information */
02355       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02356       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02357       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02358       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02359       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02360       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02361       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02362       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02363       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02364       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02365       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02366    }
02367    ast_channel_unlock(qe->chan);
02368 
02369    /* Place the call, but don't wait on the answer */
02370    if ((res = ast_call(tmp->chan, location, 0))) {
02371       /* Again, keep going even if there's an error */
02372       ast_debug(1, "ast call on peer returned %d\n", res);
02373       ast_verb(3, "Couldn't call %s\n", tmp->interface);
02374       do_hang(tmp);
02375       (*busies)++;
02376       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02377       return 0;
02378    } else if (qe->parent->eventwhencalled) {
02379       char vars[2048];
02380 
02381       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02382                "Queue: %s\r\n"
02383                "AgentCalled: %s\r\n"
02384                "AgentName: %s\r\n"
02385                "ChannelCalling: %s\r\n"
02386                "DestinationChannel: %s\r\n"
02387                "CallerIDNum: %s\r\n"
02388                "CallerIDName: %s\r\n"
02389                "Context: %s\r\n"
02390                "Extension: %s\r\n"
02391                "Priority: %d\r\n"
02392                "Uniqueid: %s\r\n"
02393                "%s",
02394                qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name,
02395                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02396                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02397                qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid,
02398                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02399       ast_verb(3, "Called %s\n", tmp->interface);
02400    }
02401 
02402    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02403    return 1;
02404 }

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

References ast_debug, callattempt::chan, queue_ent::expire, find_best(), callattempt::interface, callattempt::metric, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_entry(), callattempt::stillgoing, and call_queue::strategy.

Referenced by try_calling(), and wait_for_answer().

02433 {
02434    int ret = 0;
02435 
02436    while (ret == 0) {
02437       struct callattempt *best = find_best(outgoing);
02438       if (!best) {
02439          ast_debug(1, "Nobody left to try ringing in queue\n");
02440          break;
02441       }
02442       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02443          struct callattempt *cur;
02444          /* Ring everyone who shares this best metric (for ringall) */
02445          for (cur = outgoing; cur; cur = cur->q_next) {
02446             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02447                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02448                ret |= ring_entry(qe, cur, busies);
02449             }
02450          }
02451       } else {
02452          /* Ring just the best channel */
02453          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02454          ret = ring_entry(qe, best, busies);
02455       }
02456       
02457       /* If we have timed out, break out */
02458       if (qe->expire && (time(NULL) >= qe->expire)) {
02459          ast_debug(1, "Queue timed out while ringing members.\n");
02460          ret = 0;
02461          break;
02462       }
02463    }
02464 
02465    return ret;
02466 }

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

References ast_queue_log(), ast_verb, call_queue::autopause, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, manager_event, ast_channel::name, call_queue::name, queue_ent::parent, set_member_paused(), and ast_channel::uniqueid.

02588 {
02589    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02590    if (qe->parent->eventwhencalled)
02591       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02592                   "Queue: %s\r\n"
02593                   "Uniqueid: %s\r\n"
02594                   "Channel: %s\r\n"
02595                   "Member: %s\r\n"
02596                   "MemberName: %s\r\n"
02597                   "Ringtime: %d\r\n",
02598                   qe->parent->name,
02599                   qe->chan->uniqueid,
02600                   qe->chan->name,
02601                   interface,
02602                   membername,
02603                   rnatime);
02604    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02605    if (qe->parent->autopause && pause) {
02606       if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02607          ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02608       } else {
02609          ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02610       }
02611    }
02612    return;
02613 }

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

RemoveQueueMember application.

Definition at line 4637 of file app_queue.c.

References AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, 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().

04638 {
04639    int res=-1;
04640    char *parse, *temppos = NULL;
04641    AST_DECLARE_APP_ARGS(args,
04642       AST_APP_ARG(queuename);
04643       AST_APP_ARG(interface);
04644       AST_APP_ARG(options);
04645    );
04646 
04647 
04648    if (ast_strlen_zero(data)) {
04649       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04650       return -1;
04651    }
04652 
04653    parse = ast_strdupa(data);
04654 
04655    AST_STANDARD_APP_ARGS(args, parse);
04656 
04657    if (ast_strlen_zero(args.interface)) {
04658       args.interface = ast_strdupa(chan->name);
04659       temppos = strrchr(args.interface, '-');
04660       if (temppos)
04661          *temppos = '\0';
04662    }
04663 
04664    switch (remove_from_queue(args.queuename, args.interface)) {
04665    case RES_OKAY:
04666       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04667       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04668       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04669       res = 0;
04670       break;
04671    case RES_EXISTS:
04672       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04673       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04674       res = 0;
04675       break;
04676    case RES_NOSUCHQUEUE:
04677       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04678       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04679       res = 0;
04680       break;
04681    case RES_NOT_DYNAMIC:
04682       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04683       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04684       res = 0;
04685       break;
04686    }
04687 
04688    return res;
04689 }

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

References add_to_interfaces(), 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, remove_from_interfaces(), member::rt_uniqueid, S_OR, and member::state_interface.

Referenced by update_realtime_members().

01371 {
01372    struct member *m;
01373    struct ao2_iterator mem_iter;
01374    int penalty = 0;
01375    int paused  = 0;
01376    int found = 0;
01377 
01378    if (ast_strlen_zero(rt_uniqueid)) {
01379       ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL"));
01380       return;
01381    }
01382 
01383    if (penalty_str) {
01384       penalty = atoi(penalty_str);
01385       if (penalty < 0)
01386          penalty = 0;
01387    }
01388 
01389    if (paused_str) {
01390       paused = atoi(paused_str);
01391       if (paused < 0)
01392          paused = 0;
01393    }
01394 
01395    /* Find member by realtime uniqueid and update */
01396    mem_iter = ao2_iterator_init(q->members, 0);
01397    while ((m = ao2_iterator_next(&mem_iter))) {
01398       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01399          m->dead = 0;   /* Do not delete this one. */
01400          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01401          if (paused_str)
01402             m->paused = paused;
01403          if (strcasecmp(state_interface, m->state_interface)) {
01404             remove_from_interfaces(m->state_interface, 0);
01405             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01406             add_to_interfaces(m->state_interface);
01407          }     
01408          m->penalty = penalty;
01409          found = 1;
01410          ao2_ref(m, -1);
01411          break;
01412       }
01413       ao2_ref(m, -1);
01414    }
01415    ao2_iterator_destroy(&mem_iter);
01416 
01417    /* Create a new member */
01418    if (!found) {
01419       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01420          m->dead = 0;
01421          m->realtime = 1;
01422          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01423          add_to_interfaces(m->state_interface);
01424          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01425          ao2_link(q->members, m);
01426          ao2_ref(m, -1);
01427          m = NULL;
01428          q->membercount++;
01429       }
01430    }
01431 }

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

Playback announcement to queued members if peroid has elapsed.

Definition at line 2517 of file app_queue.c.

References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_random(), ast_strlen_zero(), 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::sound_periodicannounce, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

02518 {
02519    int res = 0;
02520    time_t now;
02521 
02522    /* Get the current time */
02523    time(&now);
02524 
02525    /* Check to see if it is time to announce */
02526    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02527       return 0;
02528 
02529    /* Stop the music on hold so we can play our own file */
02530    if (ringing)
02531       ast_indicate(qe->chan,-1);
02532    else
02533       ast_moh_stop(qe->chan);
02534 
02535    ast_verb(3, "Playing periodic announcement\n");
02536    
02537    if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) {
02538       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02539    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
02540       ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02541       qe->last_periodic_announce_sound = 0;
02542    }
02543    
02544    /* play the announcement */
02545    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02546 
02547    if (res > 0 && !valid_exit(qe, res))
02548       res = 0;
02549 
02550    /* Resume Music on Hold if the caller is going to stay in the queue */
02551    if (!res) {
02552       if (ringing)
02553          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02554       else
02555          ast_moh_start(qe->chan, qe->moh, NULL);
02556    }
02557 
02558    /* update last_periodic_announce_time */
02559    qe->last_periodic_announce_time = now;
02560 
02561    /* Update the current periodic announcement to the next announcement */
02562    if (!qe->parent->randomperiodicannounce) {
02563       qe->last_periodic_announce_sound++;
02564    }
02565    
02566    return res;
02567 }

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

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

01872 {
01873    int res = 0, avgholdmins, avgholdsecs, announceposition = 0;
01874    int say_thanks = 1;
01875    time_t now;
01876 
01877    /* Let minannouncefrequency seconds pass between the start of each position announcement */
01878    time(&now);
01879    if ((now - qe->last_pos) < qe->parent->minannouncefrequency)
01880       return 0;
01881 
01882    /* If either our position has changed, or we are over the freq timer, say position */
01883    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01884       return 0;
01885 
01886    if (ringing) {
01887       ast_indicate(qe->chan,-1);
01888    } else {
01889       ast_moh_stop(qe->chan);
01890    }
01891 
01892    if (qe->parent->announceposition == ANNOUNCEPOSITION_YES ||
01893       qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN ||
01894       (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT &&
01895       qe->pos <= qe->parent->announcepositionlimit))
01896          announceposition = 1;
01897 
01898 
01899    if (announceposition == 1) {
01900       /* Say we're next, if we are */
01901       if (qe->pos == 1) {
01902          res = play_file(qe->chan, qe->parent->sound_next);
01903          if (res)
01904             goto playout;
01905          else
01906             goto posout;
01907       } else {
01908          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01909             /* More than Case*/
01910             res = play_file(qe->chan, qe->parent->queue_quantity1);
01911             if (res)
01912                goto playout;
01913             res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
01914             if (res)
01915                goto playout;
01916          } else {
01917             /* Normal Case */
01918             res = play_file(qe->chan, qe->parent->sound_thereare);
01919             if (res)
01920                goto playout;
01921             res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */
01922             if (res)
01923                goto playout;
01924          }
01925          if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){
01926             /* More than Case*/
01927             res = play_file(qe->chan, qe->parent->queue_quantity2);
01928             if (res)
01929                goto playout;
01930          } else {
01931             res = play_file(qe->chan, qe->parent->sound_calls);
01932             if (res)
01933                goto playout;
01934          }
01935       }
01936    }
01937    /* Round hold time to nearest minute */
01938    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01939 
01940    /* If they have specified a rounding then round the seconds as well */
01941    if (qe->parent->roundingseconds) {
01942       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01943       avgholdsecs *= qe->parent->roundingseconds;
01944    } else {
01945       avgholdsecs = 0;
01946    }
01947 
01948    ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01949 
01950    /* If the hold time is >1 min, if it's enabled, and if it's not
01951       supposed to be only once and we have already said it, say it */
01952     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01953         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01954         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01955       res = play_file(qe->chan, qe->parent->sound_holdtime);
01956       if (res)
01957          goto playout;
01958 
01959       if (avgholdmins >= 1) {
01960          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01961          if (res)
01962             goto playout;
01963 
01964          if (avgholdmins == 1) {
01965             res = play_file(qe->chan, qe->parent->sound_minute);
01966             if (res)
01967                goto playout;
01968          } else {
01969             res = play_file(qe->chan, qe->parent->sound_minutes);
01970             if (res)
01971                goto playout;
01972          }
01973       }
01974       if (avgholdsecs >= 1) {
01975          res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01976          if (res)
01977             goto playout;
01978 
01979          res = play_file(qe->chan, qe->parent->sound_seconds);
01980          if (res)
01981             goto playout;
01982       }
01983    } else if (qe->parent->announceholdtime && !qe->parent->announceposition) {
01984       say_thanks = 0;
01985    }
01986 
01987 posout:
01988    if (qe->parent->announceposition) {
01989       ast_verb(3, "Told %s in %s their queue position (which was %d)\n",
01990          qe->chan->name, qe->parent->name, qe->pos);
01991    }
01992    if (say_thanks) {
01993       res = play_file(qe->chan, qe->parent->sound_thanks);
01994    }
01995 playout:
01996 
01997    if ((res > 0 && !valid_exit(qe, res)))
01998       res = 0;
01999 
02000    /* Set our last_pos indicators */
02001    qe->last_pos = now;
02002    qe->last_pos_said = qe->pos;
02003 
02004    /* Don't restart music on hold if we're about to exit the caller from the queue */
02005    if (!res) {
02006       if (ringing) {
02007          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02008       } else {
02009          ast_moh_start(qe->chan, qe->moh, NULL);
02010       }
02011    }
02012    return res;
02013 }

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

03195 {
03196    const char *reason = NULL; /* silence dumb compilers */
03197 
03198    if (!qe->parent->eventwhencalled)
03199       return;
03200 
03201    switch (rsn) {
03202    case CALLER:
03203       reason = "caller";
03204       break;
03205    case AGENT:
03206       reason = "agent";
03207       break;
03208    case TRANSFER:
03209       reason = "transfer";
03210       break;
03211    }
03212 
03213    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03214       "Queue: %s\r\n"
03215       "Uniqueid: %s\r\n"
03216       "Channel: %s\r\n"
03217       "Member: %s\r\n"
03218       "MemberName: %s\r\n"
03219       "HoldTime: %ld\r\n"
03220       "TalkTime: %ld\r\n"
03221       "Reason: %s\r\n"
03222       "%s",
03223       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03224       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03225       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03226 }

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

Definition at line 4310 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_WARNING, manager_event, member::membername, call_queue::name, member::paused, queue_t_unref, queues, member::realtime, RESULT_FAILURE, RESULT_SUCCESS, S_OR, and update_realtime_member_field().

Referenced by handle_queue_pause_member(), manager_pause_queue_member(), pqm_exec(), rna(), and upqm_exec().

04311 {
04312    int found = 0;
04313    struct call_queue *q;
04314    struct member *mem;
04315    struct ao2_iterator queue_iter;
04316    int failed;
04317 
04318    /* Special event for when all queues are paused - individual events still generated */
04319    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
04320    if (ast_strlen_zero(queuename))
04321       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04322 
04323    queue_iter = ao2_iterator_init(queues, 0);
04324    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04325       ao2_lock(q);
04326       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04327          if ((mem = interface_exists(q, interface))) {
04328             if (mem->paused == paused) {
04329                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04330             }
04331 
04332             failed = 0;
04333             if (mem->realtime) {
04334                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04335             }
04336          
04337             if (failed) {
04338                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04339                ao2_ref(mem, -1);
04340                ao2_unlock(q);
04341                queue_t_unref(q, "Done with iterator");
04342                continue;
04343             }  
04344             found++;
04345             mem->paused = paused;
04346 
04347             if (queue_persistent_members)
04348                dump_queue_members(q);
04349 
04350             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04351             
04352             if (!ast_strlen_zero(reason)) {
04353                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04354                   "Queue: %s\r\n"
04355                   "Location: %s\r\n"
04356                   "MemberName: %s\r\n"
04357                   "Paused: %d\r\n"
04358                   "Reason: %s\r\n",
04359                      q->name, mem->interface, mem->membername, paused, reason);
04360             } else {
04361                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04362                   "Queue: %s\r\n"
04363                   "Location: %s\r\n"
04364                   "MemberName: %s\r\n"
04365                   "Paused: %d\r\n",
04366                      q->name, mem->interface, mem->membername, paused);
04367             }
04368             ao2_ref(mem, -1);
04369          }
04370       }
04371       
04372       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04373          ao2_unlock(q);
04374          queue_t_unref(q, "Done with iterator");
04375          break;
04376       }
04377       
04378       ao2_unlock(q);
04379       queue_t_unref(q, "Done with iterator");
04380    }
04381    ao2_iterator_destroy(&queue_iter);
04382 
04383    return found ? RESULT_SUCCESS : RESULT_FAILURE;
04384 }

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

Definition at line 4387 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, queues, RESULT_FAILURE, and RESULT_SUCCESS.

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

04388 {
04389    int foundinterface = 0, foundqueue = 0;
04390    struct call_queue *q;
04391    struct member *mem;
04392    struct ao2_iterator queue_iter;
04393 
04394    if (penalty < 0) {
04395       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04396       return RESULT_FAILURE;
04397    }
04398 
04399    queue_iter = ao2_iterator_init(queues, 0);
04400    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
04401       ao2_lock(q);
04402       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04403          foundqueue++;
04404          if ((mem = interface_exists(q, interface))) {
04405             foundinterface++;
04406             mem->penalty = penalty;
04407             
04408             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04409             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04410                "Queue: %s\r\n"
04411                "Location: %s\r\n"
04412                "Penalty: %d\r\n",
04413                q->name, mem->interface, penalty);
04414             ao2_ref(mem, -1);
04415          }
04416       }
04417       ao2_unlock(q);
04418       queue_t_unref(q, "Done with iterator");
04419    }
04420    ao2_iterator_destroy(&queue_iter);
04421 
04422    if (foundinterface) {
04423       return RESULT_SUCCESS;
04424    } else if (!foundqueue) {
04425       ast_log (LOG_ERROR, "Invalid queuename\n"); 
04426    } else {
04427       ast_log (LOG_ERROR, "Invalid interface\n");
04428    }  
04429 
04430    return RESULT_FAILURE;
04431 }

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

sets the QUEUESTATUS channel variable

Definition at line 556 of file app_queue.c.

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

Referenced by queue_exec().

00557 {
00558    int i;
00559 
00560    for (i = 0; i < ARRAY_LEN(queue_results); i++) {
00561       if (queue_results[i].id == res) {
00562          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00563          return;
00564       }
00565    }
00566 }

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

Set variables of queue.

Definition at line 631 of file app_queue.c.

References 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, and call_queue::strategy.

Referenced by end_bridge_callback(), and record_abandoned().

00632 {
00633    char interfacevar[256]="";
00634    float sl = 0;
00635 
00636    if (q->setqueuevar) {
00637       sl = 0;
00638       if (q->callscompleted > 0) 
00639          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00640 
00641       snprintf(interfacevar, sizeof(interfacevar),
00642          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00643          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
00644    
00645       pbx_builtin_setvar_multiple(chan, interfacevar); 
00646    }
00647 }

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

03297 {
03298    struct ast_datastore *ds;
03299    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03300 
03301    if (!qtds) {
03302       ast_log(LOG_WARNING, "Memory allocation error!\n");
03303       return NULL;
03304    }
03305 
03306    ast_channel_lock(qe->chan);
03307    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03308       ast_channel_unlock(qe->chan);
03309       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03310       return NULL;
03311    }
03312 
03313    qtds->qe = qe;
03314    /* This member is refcounted in try_calling, so no need to add it here, too */
03315    qtds->member = member;
03316    qtds->starttime = starttime;
03317    qtds->callcompletedinsl = callcompletedinsl;
03318    ds->data = qtds;
03319    ast_channel_datastore_add(qe->chan, ds);
03320    ast_channel_unlock(qe->chan);
03321    return ds;
03322 }

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

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

Referenced by try_calling().

02494 {
02495    struct callattempt *best = find_best(outgoing);
02496 
02497    if (best) {
02498       /* Ring just the best channel */
02499       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02500       qe->linpos = best->metric % 1000;
02501    } else {
02502       /* Just increment rrpos */
02503       if (qe->linwrapped) {
02504          /* No more channels, start over */
02505          qe->linpos = 0;
02506       } else {
02507          /* Prioritize next entry */
02508          qe->linpos++;
02509       }
02510    }
02511    qe->linwrapped = 0;
02512 
02513    return 0;
02514 }

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

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

Referenced by try_calling().

02470 {
02471    struct callattempt *best = find_best(outgoing);
02472 
02473    if (best) {
02474       /* Ring just the best channel */
02475       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02476       qe->parent->rrpos = best->metric % 1000;
02477    } else {
02478       /* Just increment rrpos */
02479       if (qe->parent->wrapped) {
02480          /* No more channels, start over */
02481          qe->parent->rrpos = 0;
02482       } else {
02483          /* Prioritize next entry */
02484          qe->parent->rrpos++;
02485       }
02486    }
02487    qe->parent->wrapped = 0;
02488 
02489    return 0;
02490 }

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

Definition at line 580 of file app_queue.c.

References ARRAY_LEN, strategies, and strategy::strategy.

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

00581 {
00582    int x;
00583 
00584    for (x = 0; x < ARRAY_LEN(strategies); x++) {
00585       if (!strcasecmp(strategy, strategies[x].name))
00586          return strategies[x].strategy;
00587    }
00588 
00589    return -1;
00590 }

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

References ast_channel::_state, queue_ent::announce, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_calloc, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_datastore_alloc, ast_datastore_free(), ast_debug, AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_free, AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_set_flag, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, queue_ent::expire, free, ast_datastore::inheritance, member::interface, member::lastcall, member::lastqueue, ast_dialed_interface::list, call_queue::membercount, call_queue::members, call_queue::name, ast_channel::name, queue_ent::parent, queue_ent::pending, callattempt::q_next, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, queues, ring_one(), member::status, store_next_lin(), store_next_rr(), call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, and wait_for_answer().

Referenced by queue_exec().

03379 {
03380    struct member *cur;
03381    struct callattempt *outgoing = NULL; /* the list of calls we are building */
03382    int to, orig;
03383    char oldexten[AST_MAX_EXTENSION]="";
03384    char oldcontext[AST_MAX_CONTEXT]="";
03385    char queuename[256]="";
03386    char interfacevar[256]="";
03387    struct ast_channel *peer;
03388    struct ast_channel *which;
03389    struct callattempt *lpeer;
03390    struct member *member;
03391    struct ast_app *application;
03392    int res = 0, bridge = 0;
03393    int numbusies = 0;
03394    int x=0;
03395    char *announce = NULL;
03396    char digit = 0;
03397    time_t callstart;
03398    time_t now = time(NULL);
03399    struct ast_bridge_config bridge_config;
03400    char nondataquality = 1;
03401    char *agiexec = NULL;
03402    char *macroexec = NULL;
03403    char *gosubexec = NULL;
03404    int ret = 0;
03405    const char *monitorfilename;
03406    const char *monitor_exec;
03407    const char *monitor_options;
03408    char tmpid[256], tmpid2[256];
03409    char meid[1024], meid2[1024];
03410    char mixmonargs[1512];
03411    struct ast_app *mixmonapp = NULL;
03412    char *p;
03413    char vars[2048];
03414    int forwardsallowed = 1;
03415    int callcompletedinsl;
03416    struct ao2_iterator memi;
03417    struct ast_datastore *datastore, *transfer_ds;
03418    struct queue_end_bridge *queue_end_bridge = NULL;
03419    const int need_weight = use_weight;
03420 
03421    ast_channel_lock(qe->chan);
03422    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03423    ast_channel_unlock(qe->chan);
03424 
03425    memset(&bridge_config, 0, sizeof(bridge_config));
03426    tmpid[0] = 0;
03427    meid[0] = 0;
03428    time(&now);
03429 
03430    /* If we've already exceeded our timeout, then just stop
03431     * This should be extremely rare. queue_exec will take care
03432     * of removing the caller and reporting the timeout as the reason.
03433     */
03434    if (qe->expire && now >= qe->expire) {
03435       res = 0;
03436       goto out;
03437    }
03438       
03439    for (; options && *options; options++)
03440       switch (*options) {
03441       case 't':
03442          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03443          break;
03444       case 'T':
03445          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03446          break;
03447       case 'w':
03448          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03449          break;
03450       case 'W':
03451          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03452          break;
03453       case 'c':
03454          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
03455          break;
03456       case 'd':
03457          nondataquality = 0;
03458          break;
03459       case 'h':
03460          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03461          break;
03462       case 'H':
03463          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03464          break;
03465       case 'k':
03466          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL);
03467          break;
03468       case 'K':
03469          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL);
03470          break;
03471       case 'n':
03472          if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03473             (*tries)++;
03474          else
03475             *tries = qe->parent->membercount;
03476          *noption = 1;
03477          break;
03478       case 'i':
03479          forwardsallowed = 0;
03480          break;
03481       case 'x':
03482          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON);
03483          break;
03484       case 'X':
03485          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON);
03486          break;
03487 
03488       }
03489 
03490    /* Hold the lock while we setup the outgoing calls */
03491    if (need_weight)
03492       ao2_lock(queues);
03493    ao2_lock(qe->parent);
03494    ast_debug(1, "%s is trying to call a queue member.\n",
03495                      qe->chan->name);
03496    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03497    if (!ast_strlen_zero(qe->announce))
03498       announce = qe->announce;
03499    if (!ast_strlen_zero(announceoverride))
03500       announce = announceoverride;
03501 
03502    memi = ao2_iterator_init(qe->parent->members, 0);
03503    while ((cur = ao2_iterator_next(&memi))) {
03504       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03505       struct ast_dialed_interface *di;
03506       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03507       if (!tmp) {
03508          ao2_ref(cur, -1);
03509          ao2_unlock(qe->parent);
03510          ao2_iterator_destroy(&memi);
03511          if (need_weight)
03512             ao2_unlock(queues);
03513          goto out;
03514       }
03515       if (!datastore) {
03516          if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) {
03517             ao2_ref(cur, -1);
03518             ao2_unlock(qe->parent);
03519             ao2_iterator_destroy(&memi);
03520             if (need_weight)
03521                ao2_unlock(queues);
03522             free(tmp);
03523             goto out;
03524          }
03525          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03526          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03527             ao2_ref(cur, -1);
03528             ao2_unlock(&qe->parent);
03529             ao2_iterator_destroy(&memi);
03530             if (need_weight)
03531                ao2_unlock(queues);
03532             free(tmp);
03533             goto out;
03534          }
03535          datastore->data = dialed_interfaces;
03536          AST_LIST_HEAD_INIT(dialed_interfaces);
03537 
03538          ast_channel_lock(qe->chan);
03539          ast_channel_datastore_add(qe->chan, datastore);
03540          ast_channel_unlock(qe->chan);
03541       } else
03542          dialed_interfaces = datastore->data;
03543 
03544       AST_LIST_LOCK(dialed_interfaces);
03545       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03546          if (!strcasecmp(cur->interface, di->interface)) {
03547             ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 
03548                di->interface);
03549             break;
03550          }
03551       }
03552       AST_LIST_UNLOCK(dialed_interfaces);
03553       
03554       if (di) {
03555          free(tmp);
03556          continue;
03557       }
03558 
03559       /* It is always ok to dial a Local interface.  We only keep track of
03560        * which "real" interfaces have been dialed.  The Local channel will
03561        * inherit this list so that if it ends up dialing a real interface,
03562        * it won't call one that has already been called. */
03563       if (strncasecmp(cur->interface, "Local/", 6)) {
03564          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03565             ao2_ref(cur, -1);
03566             ao2_unlock(qe->parent);
03567             ao2_iterator_destroy(&memi);
03568             if (need_weight)
03569                ao2_unlock(queues);
03570             free(tmp);
03571             goto out;
03572          }
03573          strcpy(di->interface, cur->interface);
03574 
03575          AST_LIST_LOCK(dialed_interfaces);
03576          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03577          AST_LIST_UNLOCK(dialed_interfaces);
03578       }
03579 
03580       tmp->stillgoing = -1;
03581       tmp->member = cur;
03582       tmp->oldstatus = cur->status;
03583       tmp->lastcall = cur->lastcall;
03584       tmp->lastqueue = cur->lastqueue;
03585       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03586       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03587          just calculate their metric for the appropriate strategy */
03588       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03589          /* Put them in the list of outgoing thingies...  We're ready now.
03590             XXX If we're forcibly removed, these outgoing calls won't get
03591             hung up XXX */
03592          tmp->q_next = outgoing;
03593          outgoing = tmp;      
03594          /* If this line is up, don't try anybody else */
03595          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03596             break;
03597       } else {
03598          ao2_ref(cur, -1);
03599          ast_free(tmp);
03600       }
03601    }
03602    ao2_iterator_destroy(&memi);
03603 
03604    if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) {
03605       /* Application arguments have higher timeout priority (behaviour for <=1.6) */
03606       if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03607          to = (qe->expire - now) * 1000;
03608       else
03609          to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03610    } else {
03611       /* Config timeout is higher priority thatn application timeout */
03612       if (qe->expire && qe->expire<=now) {
03613          to = 0;
03614       } else if (qe->parent->timeout) {
03615          to = qe->parent->timeout * 1000;
03616       } else {
03617          to = -1;
03618       }
03619    }
03620    orig = to;
03621    ++qe->pending;
03622    ao2_unlock(qe->parent);
03623    ring_one(qe, outgoing, &numbusies);
03624    if (need_weight)
03625       ao2_unlock(queues);
03626    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03627    /* The ast_channel_datastore_remove() function could fail here if the
03628     * datastore was moved to another channel during a masquerade. If this is
03629     * the case, don't free the datastore here because later, when the channel
03630     * to which the datastore was moved hangs up, it will attempt to free this
03631     * datastore again, causing a crash
03632     */
03633    ast_channel_lock(qe->chan);
03634    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03635       ast_datastore_free(datastore);
03636    }
03637    ast_channel_unlock(qe->chan);
03638    ao2_lock(qe->parent);
03639    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03640       store_next_rr(qe, outgoing);
03641    }
03642    if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03643       store_next_lin(qe, outgoing);
03644    }
03645    ao2_unlock(qe->parent);
03646    peer = lpeer ? lpeer->chan : NULL;
03647    if (!peer) {
03648       qe->pending = 0;
03649       if (to) {
03650          /* Must gotten hung up */
03651          res = -1;
03652       } else {
03653          /* User exited by pressing a digit */
03654          res = digit;
03655       }
03656       if (res == -1)
03657          ast_debug(1, "%s: Nobody answered.\n", qe->chan->name);
03658       if (ast_cdr_isset_unanswered()) {
03659          /* channel contains the name of one of the outgoing channels
03660             in its CDR; zero out this CDR to avoid a dual-posting */
03661          struct callattempt *o;
03662          for (o = outgoing; o; o = o->q_next) {
03663             if (!o->chan) {
03664                continue;
03665             }
03666             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03667                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03668                break;
03669             }
03670          }
03671       }
03672    } else { /* peer is valid */
03673       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03674          we will always return with -1 so that it is hung up properly after the
03675          conversation.  */
03676       if (!strcmp(qe->chan->tech->type, "DAHDI"))
03677          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03678       if (!strcmp(peer->tech->type, "DAHDI"))
03679          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03680       /* Update parameters for the queue */
03681       time(&now);
03682       recalc_holdtime(qe, (now - qe->start));
03683       ao2_lock(qe->parent);
03684       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03685       ao2_unlock(qe->parent);
03686       member = lpeer->member;
03687       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03688       ao2_ref(member, 1);
03689       hangupcalls(outgoing, peer);
03690       outgoing = NULL;
03691       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03692          int res2;
03693 
03694          res2 = ast_autoservice_start(qe->chan);
03695          if (!res2) {
03696             if (qe->parent->memberdelay) {
03697                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03698                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03699             }
03700             if (!res2 && announce) {
03701                play_file(peer, announce);
03702             }
03703             if (!res2 && qe->parent->reportholdtime) {
03704                if (!play_file(peer, qe->parent->sound_reporthold)) {
03705                   int holdtime, holdtimesecs;
03706 
03707                   time(&now);
03708                   holdtime = abs((now - qe->start) / 60);
03709                   holdtimesecs = abs((now - qe->start) % 60);
03710                   if (holdtime > 0) {
03711                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03712                      play_file(peer, qe->parent->sound_minutes);
03713                   }
03714                   if (holdtimesecs > 1) {
03715                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03716                      play_file(peer, qe->parent->sound_seconds);
03717                   }
03718                }
03719             }
03720          }
03721          res2 |= ast_autoservice_stop(qe->chan);
03722          if (ast_check_hangup(peer)) {
03723             /* Agent must have hung up */
03724             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03725             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03726             if (qe->parent->eventwhencalled)
03727                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03728                      "Queue: %s\r\n"
03729                      "Uniqueid: %s\r\n"
03730                      "Channel: %s\r\n"
03731                      "Member: %s\r\n"
03732                      "MemberName: %s\r\n"
03733                      "%s",
03734                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03735                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03736             ast_hangup(peer);
03737             ao2_ref(member, -1);
03738             goto out;
03739          } else if (res2) {
03740             /* Caller must have hung up just before being connected*/
03741             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03742             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03743             record_abandoned(qe);
03744             ast_cdr_noanswer(qe->chan->cdr);
03745             ast_hangup(peer);
03746             ao2_ref(member, -1);
03747             return -1;
03748          }
03749       }
03750       /* Stop music on hold */
03751       if (ringing)
03752          ast_indicate(qe->chan,-1);
03753       else
03754          ast_moh_stop(qe->chan);
03755       /* If appropriate, log that we have a destination channel */
03756       if (qe->chan->cdr)
03757          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03758       /* Make sure channels are compatible */
03759       res = ast_channel_make_compatible(qe->chan, peer);
03760       if (res < 0) {
03761          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03762          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03763          record_abandoned(qe);
03764          ast_cdr_failed(qe->chan->cdr);
03765          ast_hangup(peer);
03766          ao2_ref(member, -1);
03767          return -1;
03768       }
03769 
03770       /* Play announcement to the caller telling it's his turn if defined */
03771       if (!ast_strlen_zero(qe->parent->sound_callerannounce)) {
03772          if (play_file(qe->chan, qe->parent->sound_callerannounce))
03773             ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce);
03774       }
03775 
03776       ao2_lock(qe->parent);
03777       /* if setinterfacevar is defined, make member variables available to the channel */
03778       /* use  pbx_builtin_setvar to set a load of variables with one call */
03779       if (qe->parent->setinterfacevar) {
03780          snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d",
03781             member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime);
03782          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03783          pbx_builtin_setvar_multiple(peer, interfacevar);
03784       }
03785       
03786       /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */
03787       /* use  pbx_builtin_setvar to set a load of variables with one call */
03788       if (qe->parent->setqueueentryvar) {
03789          snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d",
03790             (long) time(NULL) - qe->start, qe->opos);
03791          pbx_builtin_setvar_multiple(qe->chan, interfacevar);
03792          pbx_builtin_setvar_multiple(peer, interfacevar);
03793       }
03794    
03795       /* try to set queue variables if configured to do so*/
03796       set_queue_variables(qe->parent, qe->chan);
03797       set_queue_variables(qe->parent, peer);
03798       ao2_unlock(qe->parent);
03799       
03800       ast_channel_lock(qe->chan);
03801       if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
03802             monitorfilename = ast_strdupa(monitorfilename);
03803       }
03804       ast_channel_unlock(qe->chan);
03805       /* Begin Monitoring */
03806       if (qe->parent->monfmt && *qe->parent->monfmt) {
03807          if (!qe->parent->montype) {
03808             const char *monexec, *monargs;
03809             ast_debug(1, "Starting Monitor as requested.\n");
03810             ast_channel_lock(qe->chan);
03811             if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) {
03812                which = qe->chan;
03813                monexec = monexec ? ast_strdupa(monexec) : NULL;
03814             }
03815             else
03816                which = peer;
03817             ast_channel_unlock(qe->chan);
03818             if (ast_monitor_start) {
03819                if (monitorfilename) {
03820                   ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT);
03821                } else if (qe->chan->cdr && ast_monitor_start) {
03822                   ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT);
03823                } else if (ast_monitor_start) {
03824                   /* Last ditch effort -- no CDR, make up something */
03825                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03826                   ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT);
03827                }
03828             }
03829             if (!ast_strlen_zero(monexec) && ast_monitor_setjoinfiles) {
03830                ast_monitor_setjoinfiles(which, 1);
03831             }
03832          } else {
03833             mixmonapp = pbx_findapp("MixMonitor");
03834             
03835             if (mixmonapp) {
03836                ast_debug(1, "Starting MixMonitor as requested.\n");
03837                if (!monitorfilename) {
03838                   if (qe->chan->cdr)
03839                      ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid));
03840                   else
03841                      snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03842                } else {
03843                   const char *m = monitorfilename;
03844                   for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) {
03845                      switch (*m) {
03846                      case '^':
03847                         if (*(m + 1) == '{')
03848                            *p = '$';
03849                         break;
03850                      case ',':
03851                         *p++ = '\\';
03852                         /* Fall through */
03853                      default:
03854                         *p = *m;
03855                      }
03856                      if (*m == '\0')
03857                         break;
03858                   }
03859                   if (p == tmpid2 + sizeof(tmpid2))
03860                      tmpid2[sizeof(tmpid2) - 1] = '\0';
03861 
03862                   pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03863                }
03864 
03865                ast_channel_lock(qe->chan);
03866                if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) {
03867                      monitor_exec = ast_strdupa(monitor_exec);
03868                }
03869                if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) {
03870                      monitor_options = ast_strdupa(monitor_options);
03871                } else {
03872                   monitor_options = "";
03873                }
03874                ast_channel_unlock(qe->chan);
03875 
03876                if (monitor_exec) {
03877                   const char *m = monitor_exec;
03878                   for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) {
03879                      switch (*m) {
03880                      case '^':
03881                         if (*(m + 1) == '{')
03882                            *p = '$';
03883                         break;
03884                      case ',':
03885                         *p++ = '\\';
03886                         /* Fall through */
03887                      default:
03888                         *p = *m;
03889                      }
03890                      if (*m == '\0')
03891                         break;
03892                   }
03893                   if (p == meid2 + sizeof(meid2))
03894                      meid2[sizeof(meid2) - 1] = '\0';
03895 
03896                   pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03897                }
03898    
03899                snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt);
03900 
03901                if (!ast_strlen_zero(monitor_exec))
03902                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec);
03903                else
03904                   snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options);
03905                
03906                ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03907                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03908                if (qe->chan->cdr)
03909                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03910                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03911                if (qe->chan->cdr)
03912                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03913 
03914             } else {
03915                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03916             }
03917          }
03918       }
03919       /* Drop out of the queue at this point, to prepare for next caller */
03920       leave_queue(qe);        
03921       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03922          ast_debug(1, "app_queue: sendurl=%s.\n", url);
03923          ast_channel_sendurl(peer, url);
03924       }
03925       
03926       /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */
03927       /* use macro from dialplan if passed as a option, otherwise use the default queue macro */
03928       if (!ast_strlen_zero(macro)) {
03929             macroexec = ast_strdupa(macro);
03930       } else {
03931          if (qe->parent->membermacro)
03932             macroexec = ast_strdupa(qe->parent->membermacro);
03933       }
03934 
03935       if (!ast_strlen_zero(macroexec)) {
03936          ast_debug(1, "app_queue: macro=%s.\n", macroexec);
03937          
03938          res = ast_autoservice_start(qe->chan);
03939          if (res) {
03940             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03941             res = -1;
03942          }
03943          
03944          application = pbx_findapp("Macro");
03945 
03946          if (application) {
03947             res = pbx_exec(peer, application, macroexec);
03948             ast_debug(1, "Macro exited with status %d\n", res);
03949             res = 0;
03950          } else {
03951             ast_log(LOG_ERROR, "Could not find application Macro\n");
03952             res = -1;
03953          }
03954 
03955          if (ast_autoservice_stop(qe->chan) < 0) {
03956             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
03957             res = -1;
03958          }
03959       }
03960 
03961       /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */
03962       /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */
03963       if (!ast_strlen_zero(gosub)) {
03964             gosubexec = ast_strdupa(gosub);
03965       } else {
03966          if (qe->parent->membergosub)
03967             gosubexec = ast_strdupa(qe->parent->membergosub);
03968       }
03969 
03970       if (!ast_strlen_zero(gosubexec)) {
03971          ast_debug(1, "app_queue: gosub=%s.\n", gosubexec);
03972          
03973          res = ast_autoservice_start(qe->chan);
03974          if (res) {
03975             ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n");
03976             res = -1;
03977          }
03978          
03979          application = pbx_findapp("Gosub");
03980          
03981          if (application) {
03982             char *gosub_args, *gosub_argstart;
03983 
03984             /* Set where we came from */
03985             ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context));
03986             ast_copy_string(peer->exten, "s", sizeof(peer->exten));
03987             peer->priority = 0;
03988 
03989             gosub_argstart = strchr(gosubexec, ',');
03990             if (gosub_argstart) {
03991                *gosub_argstart = 0;
03992                if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) {
03993                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
03994                   gosub_args = NULL;
03995                }
03996                *gosub_argstart = ',';
03997             } else {
03998                if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) {
03999                   ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno));
04000                   gosub_args = NULL;
04001                }
04002             }
04003             if (gosub_args) {
04004                res = pbx_exec(peer, application, gosub_args);
04005                if (!res) {
04006                   struct ast_pbx_args args;
04007                   memset(&args, 0, sizeof(args));
04008                   args.no_hangup_chan = 1;
04009                   ast_pbx_run_args(peer, &args);
04010                }
04011                ast_free(gosub_args);
04012                ast_debug(1, "Gosub exited with status %d\n", res);
04013             } else {
04014                ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n");
04015             }
04016          } else {
04017             ast_log(LOG_ERROR, "Could not find application Gosub\n");
04018             res = -1;
04019          }
04020       
04021          if (ast_autoservice_stop(qe->chan) < 0) {
04022             ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n");
04023             res = -1;
04024          }
04025       }
04026 
04027       if (!ast_strlen_zero(agi)) {
04028          ast_debug(1, "app_queue: agi=%s.\n", agi);
04029          application = pbx_findapp("agi");
04030          if (application) {
04031             agiexec = ast_strdupa(agi);
04032             ret = pbx_exec(qe->chan, application, agiexec);
04033          } else
04034             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
04035       }
04036       qe->handled++;
04037       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid,
04038                                        (long)(orig - to > 0 ? (orig - to) / 1000 : 0));
04039       if (update_cdr && qe->chan->cdr) 
04040          ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel));
04041       if (qe->parent->eventwhencalled)
04042          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
04043                "Queue: %s\r\n"
04044                "Uniqueid: %s\r\n"
04045                "Channel: %s\r\n"
04046                "Member: %s\r\n"
04047                "MemberName: %s\r\n"
04048                "Holdtime: %ld\r\n"
04049                "BridgedChannel: %s\r\n"
04050                "Ringtime: %ld\r\n"
04051                "%s",
04052                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
04053                (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0),
04054                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
04055       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
04056       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
04057    
04058       if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) {
04059          queue_end_bridge->q = qe->parent;
04060          queue_end_bridge->chan = qe->chan;
04061          bridge_config.end_bridge_callback = end_bridge_callback;
04062          bridge_config.end_bridge_callback_data = queue_end_bridge;
04063          bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup;
04064          /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need
04065           * to make sure to increase the refcount of this queue so it cannot be freed until we
04066           * are done with it. We remove this reference in end_bridge_callback.
04067           */
04068          queue_t_ref(qe->parent, "For bridge_config reference");
04069       }
04070 
04071       time(&callstart);
04072       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
04073       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
04074 
04075       /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
04076        * when the masquerade occurred. These other "ending" queue_log messages are unnecessary
04077        */
04078       ast_channel_lock(qe->chan);
04079       if (!attended_transfer_occurred(qe->chan)) {
04080          struct ast_datastore *tds;
04081 
04082          /* detect a blind transfer */
04083          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
04084             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
04085                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
04086                (long) (time(NULL) - callstart), qe->opos);
04087             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
04088          } else if (ast_check_hangup(qe->chan)) {
04089             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
04090                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04091             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER);
04092          } else {
04093             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
04094                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
04095             send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT);
04096          }
04097          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
04098             ast_channel_datastore_remove(qe->chan, tds);
04099          }
04100          update_queue(qe->parent, member, callcompletedinsl);
04101       }
04102 
04103       if (transfer_ds) {
04104          ast_datastore_free(transfer_ds);
04105       }
04106       ast_channel_unlock(qe->chan);
04107       ast_hangup(peer);
04108       res = bridge ? bridge : 1;
04109       ao2_ref(member, -1);
04110    }
04111 out:
04112    hangupcalls(outgoing, NULL);
04113 
04114    return res;
04115 }

static int unload_module ( void   )  [static]

Definition at line 6750 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ao2_t_iterator_next, ast_cli_unregister_multiple(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_event_unsubscribe(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state_sub, devicestate_tps, queue_t_unref, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queues_t_unlink, queuevar_function, and queuewaitingcount_function.

06751 {
06752    int res;
06753    struct ast_context *con;
06754    struct ao2_iterator q_iter;
06755    struct call_queue *q = NULL;
06756 
06757    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06758    res = ast_manager_unregister("QueueStatus");
06759    res |= ast_manager_unregister("Queues");
06760    res |= ast_manager_unregister("QueueRule");
06761    res |= ast_manager_unregister("QueueSummary");
06762    res |= ast_manager_unregister("QueueAdd");
06763    res |= ast_manager_unregister("QueueRemove");
06764    res |= ast_manager_unregister("QueuePause");
06765    res |= ast_manager_unregister("QueueLog");
06766    res |= ast_manager_unregister("QueuePenalty");
06767    res |= ast_unregister_application(app_aqm);
06768    res |= ast_unregister_application(app_rqm);
06769    res |= ast_unregister_application(app_pqm);
06770    res |= ast_unregister_application(app_upqm);
06771    res |= ast_unregister_application(app_ql);
06772    res |= ast_unregister_application(app);
06773    res |= ast_custom_function_unregister(&queuevar_function);
06774    res |= ast_custom_function_unregister(&queuemembercount_function);
06775    res |= ast_custom_function_unregister(&queuemembercount_dep);
06776    res |= ast_custom_function_unregister(&queuememberlist_function);
06777    res |= ast_custom_function_unregister(&queuewaitingcount_function);
06778    res |= ast_custom_function_unregister(&queuememberpenalty_function);
06779 
06780    if (device_state_sub)
06781       ast_event_unsubscribe(device_state_sub);
06782 
06783    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06784       ast_context_remove_extension2(con, "s", 1, NULL, 0);
06785       ast_context_destroy(con, "app_queue"); /* leave no trace */
06786    }
06787 
06788    clear_and_free_interfaces();
06789 
06790    q_iter = ao2_iterator_init(queues, 0);
06791    while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) {
06792       queues_t_unlink(queues, q, "Remove queue from container due to unload");
06793       queue_t_unref(q, "Done with iterator");
06794    }
06795    ao2_iterator_destroy(&q_iter);
06796    ao2_ref(queues, -1);
06797    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06798    ast_unload_realtime("queue_members");
06799    return res;
06800 }

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 2944 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 join_queue(), queue_exec(), and wait_our_turn().

02945 {
02946    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02947    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02948    char max_penalty_str[20], min_penalty_str[20]; 
02949    /* a relative change to the penalty could put it below 0 */
02950    if (max_penalty < 0)
02951       max_penalty = 0;
02952    if (min_penalty < 0)
02953       min_penalty = 0;
02954    if (min_penalty > max_penalty)
02955       min_penalty = max_penalty;
02956    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02957    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02958    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02959    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02960    qe->max_penalty = max_penalty;
02961    qe->min_penalty = min_penalty;
02962    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);
02963    qe->pr = AST_LIST_NEXT(qe->pr, list);
02964 }

static int update_queue ( struct call_queue q,
struct member member,
int  callcompletedinsl 
) [static]

update the queue status

Return values:
Always 0

Definition at line 3085 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, and queues.

Referenced by queue_transfer_fixup().

03086 {
03087    struct member *mem;
03088    struct call_queue *qtmp;
03089    struct ao2_iterator queue_iter;  
03090    
03091    if (shared_lastcall) {
03092       queue_iter = ao2_iterator_init(queues, 0);
03093       while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
03094          ao2_lock(qtmp);
03095          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03096             time(&mem->lastcall);
03097             mem->calls++;
03098             mem->lastqueue = q;
03099             ao2_ref(mem, -1);
03100          }
03101          ao2_unlock(qtmp);
03102          ao2_ref(qtmp, -1);
03103       }
03104       ao2_iterator_destroy(&queue_iter);
03105    } else {
03106       ao2_lock(q);
03107       time(&member->lastcall);
03108       member->calls++;
03109       member->lastqueue = q;
03110       ao2_unlock(q);
03111    }  
03112    ao2_lock(q);
03113    q->callscompleted++;
03114    if (callcompletedinsl)
03115       q->callscompletedinsl++;
03116    ao2_unlock(q);
03117    return 0;
03118 }

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

Definition at line 1669 of file app_queue.c.

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

Referenced by set_member_paused().

01670 {
01671    int ret = -1;
01672 
01673    if (ast_strlen_zero(mem->rt_uniqueid))
01674       return ret;
01675 
01676    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
01677       ret = 0;
01678 
01679    return ret;
01680 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1683 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, remove_from_interfaces(), rt_handle_member_record(), S_OR, SENTINEL, and member::state_interface.

Referenced by load_realtime_queue(), and queue_exec().

01684 {
01685    struct ast_config *member_config = NULL;
01686    struct member *m;
01687    char *interface = NULL;
01688    struct ao2_iterator mem_iter;
01689 
01690    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
01691       /*This queue doesn't have realtime members*/
01692       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
01693       return;
01694    }
01695 
01696    ao2_lock(queues);
01697    ao2_lock(q);
01698    
01699    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01700    mem_iter = ao2_iterator_init(q->members, 0);
01701    while ((m = ao2_iterator_next(&mem_iter))) {
01702       if (m->realtime)
01703          m->dead = 1;
01704       ao2_ref(m, -1);
01705    }
01706    ao2_iterator_destroy(&mem_iter);
01707 
01708    while ((interface = ast_category_browse(member_config, interface))) {
01709       rt_handle_member_record(q, interface,
01710          ast_variable_retrieve(member_config, interface, "uniqueid"),
01711          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01712          ast_variable_retrieve(member_config, interface, "penalty"),
01713          ast_variable_retrieve(member_config, interface, "paused"),
01714          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01715    }
01716 
01717    /* Delete all realtime members that have been deleted in DB. */
01718    mem_iter = ao2_iterator_init(q->members, 0);
01719    while ((m = ao2_iterator_next(&mem_iter))) {
01720       if (m->dead) {
01721          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01722          ao2_unlink(q->members, m);
01723          remove_from_interfaces(m->state_interface, 0);
01724          q->membercount--;
01725       }
01726       ao2_ref(m, -1);
01727    }
01728    ao2_iterator_destroy(&mem_iter);
01729    ao2_unlock(q);
01730    ao2_unlock(queues);
01731    ast_config_destroy(member_config);
01732 }

static int update_status ( const char *  interface,
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 736 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_copy_string(), member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event, call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, queue_t_unref, queues, member::realtime, member::state_interface, and member::status.

Referenced by handle_statechange(), and ring_entry().

00737 {
00738    struct member *cur;
00739    struct ao2_iterator mem_iter, queue_iter;
00740    struct call_queue *q;
00741    char tmp_interface[80];
00742 
00743    queue_iter = ao2_iterator_init(queues, 0);
00744    while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) {
00745       ao2_lock(q);
00746       mem_iter = ao2_iterator_init(q->members, 0);
00747       while ((cur = ao2_iterator_next(&mem_iter))) {
00748          char *slash_pos;
00749          ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
00750          if ((slash_pos = strchr(tmp_interface, '/')))
00751             if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/')))
00752                *slash_pos = '\0';
00753 
00754          if (strcasecmp(interface, tmp_interface)) {
00755             ao2_ref(cur, -1);
00756             continue;
00757          }
00758 
00759          if (cur->status != status) {
00760             cur->status = status;
00761             if (q->maskmemberstatus) {
00762                ao2_ref(cur, -1);
00763                continue;
00764             }
00765 
00766             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00767                "Queue: %s\r\n"
00768                "Location: %s\r\n"
00769                "MemberName: %s\r\n"
00770                "Membership: %s\r\n"
00771                "Penalty: %d\r\n"
00772                "CallsTaken: %d\r\n"
00773                "LastCall: %d\r\n"
00774                "Status: %d\r\n"
00775                "Paused: %d\r\n",
00776                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00777                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00778          }
00779          ao2_ref(cur, -1);
00780       }
00781       queue_t_unref(q, "Done with iterator");
00782       ao2_unlock(q);
00783    }
00784 
00785    return 0;
00786 }

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

UnPauseQueueMember application.

Definition at line 4601 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().

Referenced by load_module().

04602 {
04603    char *parse;
04604    AST_DECLARE_APP_ARGS(args,
04605       AST_APP_ARG(queuename);
04606       AST_APP_ARG(interface);
04607       AST_APP_ARG(options);
04608       AST_APP_ARG(reason);
04609    );
04610 
04611    if (ast_strlen_zero(data)) {
04612       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04613       return -1;
04614    }
04615 
04616    parse = ast_strdupa(data);
04617 
04618    AST_STANDARD_APP_ARGS(args, parse);
04619 
04620    if (ast_strlen_zero(args.interface)) {
04621       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04622       return -1;
04623    }
04624 
04625    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04626       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04627       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04628       return 0;
04629    }
04630 
04631    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04632 
04633    return 0;
04634 }

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

References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::context, queue_ent::digits, and queue_ent::valid_digits.

Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), and wait_our_turn().

01839 {
01840    int digitlen = strlen(qe->digits);
01841 
01842    /* Prevent possible buffer overflow */
01843    if (digitlen < sizeof(qe->digits) - 2) {
01844       qe->digits[digitlen] = digit;
01845       qe->digits[digitlen + 1] = '\0';
01846    } else {
01847       qe->digits[0] = '\0';
01848       return 0;
01849    }
01850 
01851    /* If there's no context to goto, short-circuit */
01852    if (ast_strlen_zero(qe->context))
01853       return 0;
01854 
01855    /* If the extension is bad, then reset the digits to blank */
01856    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01857       qe->digits[0] = '\0';
01858       return 0;
01859    }
01860 
01861    /* We have an exact match */
01862    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01863       qe->valid_digits = 1;
01864       /* Return 1 on a successful goto */
01865       return 1;
01866    }
01867 
01868    return 0;
01869 }

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

References ast_copy_string(), ast_str_alloca, buf, queue_ent::chan, and pbx_builtin_serialize_variables().

Referenced by ring_entry(), and send_agent_complete().

02204 {
02205    struct ast_str *buf = ast_str_alloca(len + 1);
02206    char *tmp;
02207 
02208    if (pbx_builtin_serialize_variables(chan, &buf)) {
02209       int i, j;
02210 
02211       /* convert "\n" to "\nVariable: " */
02212       strcpy(vars, "Variable: ");
02213       tmp = buf->str;
02214 
02215       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02216          vars[j] = tmp[i];
02217 
02218          if (tmp[i + 1] == '\0')
02219             break;
02220          if (tmp[i] == '\n') {
02221             vars[j++] = '\r';
02222             vars[j++] = '\n';
02223 
02224             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02225             j += 9;
02226          }
02227       }
02228       if (j > len - 3)
02229          j = len - 3;
02230       vars[j++] = '\r';
02231       vars[j++] = '\n';
02232       vars[j] = '\0';
02233    } else {
02234       /* there are no channel variables; leave it blank */
02235       *vars = '\0';
02236    }
02237    return vars;
02238 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 4117 of file app_queue.c.

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

Referenced by queue_exec().

04118 {
04119    /* Don't need to hold the lock while we setup the outgoing calls */
04120    int retrywait = qe->parent->retry * 1000;
04121 
04122    int res = ast_waitfordigit(qe->chan, retrywait);
04123    if (res > 0 && !valid_exit(qe, res))
04124       res = 0;
04125 
04126    return res;
04127 }

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

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

Definition at line 2626 of file app_queue.c.

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

02627 {
02628    const char *queue = qe->parent->name;
02629    struct callattempt *o, *start = NULL, *prev = NULL;
02630    int status;
02631    int numbusies = prebusies;
02632    int numnochan = 0;
02633    int stillgoing = 0;
02634    int orig = *to;
02635    struct ast_frame *f;
02636    struct callattempt *peer = NULL;
02637    struct ast_channel *winner;
02638    struct ast_channel *in = qe->chan;
02639    char on[80] = "";
02640    char membername[80] = "";
02641    long starttime = 0;
02642    long endtime = 0;
02643 #ifdef HAVE_EPOLL
02644    struct callattempt *epollo;
02645 #endif
02646 
02647    starttime = (long) time(NULL);
02648 #ifdef HAVE_EPOLL
02649    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02650       if (epollo->chan)
02651          ast_poll_channel_add(in, epollo->chan);
02652    }
02653 #endif
02654    
02655    while (*to && !peer) {
02656       int numlines, retry, pos = 1;
02657       struct ast_channel *watchers[AST_MAX_WATCHERS];
02658       watchers[0] = in;
02659       start = NULL;
02660 
02661       for (retry = 0; retry < 2; retry++) {
02662          numlines = 0;
02663          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02664             if (o->stillgoing) { /* Keep track of important channels */
02665                stillgoing = 1;
02666                if (o->chan) {
02667                   watchers[pos++] = o->chan;
02668                   if (!start)
02669                      start = o;
02670                   else
02671                      prev->call_next = o;
02672                   prev = o;
02673                }
02674             }
02675             numlines++;
02676          }
02677          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02678             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02679             break;
02680          /* On "ringall" strategy we only move to the next penalty level
02681             when *all* ringing phones are done in the current penalty level */
02682          ring_one(qe, outgoing, &numbusies);
02683          /* and retry... */
02684       }
02685       if (pos == 1 /* not found */) {
02686          if (numlines == (numbusies + numnochan)) {
02687             ast_debug(1, "Everyone is busy at this time\n");
02688          } else {
02689             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02690          }
02691          *to = 0;
02692          return NULL;
02693       }
02694 
02695       /* Poll for events from both the incoming channel as well as any outgoing channels */
02696       winner = ast_waitfor_n(watchers, pos, to);
02697 
02698       /* Service all of the outgoing channels */
02699       for (o = start; o; o = o->call_next) {
02700          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02701             if (!peer) {
02702                ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02703                peer = o;
02704             }
02705          } else if (o->chan && (o->chan == winner)) {
02706 
02707             ast_copy_string(on, o->member->interface, sizeof(on));
02708             ast_copy_string(membername, o->member->membername, sizeof(membername));
02709 
02710             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02711                ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02712                numnochan++;
02713                do_hang(o);
02714                winner = NULL;
02715                continue;
02716             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02717                char tmpchan[256];
02718                char *stuff;
02719                char *tech;
02720 
02721                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02722                if ((stuff = strchr(tmpchan, '/'))) {
02723                   *stuff++ = '\0';
02724                   tech = tmpchan;
02725                } else {
02726                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02727                   stuff = tmpchan;
02728                   tech = "Local";
02729                }
02730                /* Before processing channel, go ahead and check for forwarding */
02731                ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02732                /* Setup parameters */
02733                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02734                if (!o->chan) {
02735                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02736                   o->stillgoing = 0;
02737                   numnochan++;
02738                } else {
02739                   ast_channel_inherit_variables(in, o->chan);
02740                   ast_channel_datastore_inherit(in, o->chan);
02741                   if (o->chan->cid.cid_num)
02742                      ast_free(o->chan->cid.cid_num);
02743                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02744 
02745                   if (o->chan->cid.cid_name)
02746                      ast_free(o->chan->cid.cid_name);
02747                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02748 
02749                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02750                   o->chan->cdrflags = in->cdrflags;
02751 
02752                   if (in->cid.cid_ani) {
02753                      if (o->chan->cid.cid_ani)
02754                         ast_free(o->chan->cid.cid_ani);
02755                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02756                   }
02757                   if (o->chan->cid.cid_rdnis)
02758                      ast_free(o->chan->cid.cid_rdnis);
02759                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02760                   if (ast_call(o->chan, tmpchan, 0)) {
02761                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02762                      do_hang(o);
02763                      numnochan++;
02764                   }
02765                }
02766                /* Hangup the original channel now, in case we needed it */
02767                ast_hangup(winner);
02768                continue;
02769             }
02770             f = ast_read(winner);
02771             if (f) {
02772                if (f->frametype == AST_FRAME_CONTROL) {
02773                   switch (f->subclass) {
02774                   case AST_CONTROL_ANSWER:
02775                      /* This is our guy if someone answered. */
02776                      if (!peer) {
02777                         ast_verb(3, "%s answered %s\n", o->chan->name, in->name);
02778                         peer = o;
02779                      }
02780                      break;
02781                   case AST_CONTROL_BUSY:
02782                      ast_verb(3, "%s is busy\n", o->chan->name);
02783                      if (in->cdr)
02784                         ast_cdr_busy(in->cdr);
02785                      do_hang(o);
02786                      endtime = (long) time(NULL);
02787                      endtime -= starttime;
02788                      rna(endtime * 1000, qe, on, membername, 0);
02789                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02790                         if (qe->parent->timeoutrestart)
02791                            *to = orig;
02792                         /* Have enough time for a queue member to answer? */
02793                         if (*to > 500) {
02794                            ring_one(qe, outgoing, &numbusies);
02795                            starttime = (long) time(NULL);
02796                         }
02797                      }
02798                      numbusies++;
02799                      break;
02800                   case AST_CONTROL_CONGESTION:
02801                      ast_verb(3, "%s is circuit-busy\n", o->chan->name);
02802                      if (in->cdr)
02803                         ast_cdr_busy(in->cdr);
02804                      endtime = (long) time(NULL);
02805                      endtime -= starttime;
02806                      rna(endtime * 1000, qe, on, membername, 0);
02807                      do_hang(o);
02808                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02809                         if (qe->parent->timeoutrestart)
02810                            *to = orig;
02811                         if (*to > 500) {
02812                            ring_one(qe, outgoing, &numbusies);
02813                            starttime = (long) time(NULL);
02814                         }
02815                      }
02816                      numbusies++;
02817                      break;
02818                   case AST_CONTROL_RINGING:
02819                      ast_verb(3, "%s is ringing\n", o->chan->name);
02820                      break;
02821                   case AST_CONTROL_OFFHOOK:
02822                      /* Ignore going off hook */
02823                      break;
02824                   default:
02825                      ast_debug(1, "Dunno what to do with control type %d\n", f->subclass);
02826                   }
02827                }
02828                ast_frfree(f);
02829             } else { /* ast_read() returned NULL */
02830                endtime = (long) time(NULL) - starttime;
02831                rna(endtime * 1000, qe, on, membername, 1);
02832                do_hang(o);
02833                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02834                   if (qe->parent->timeoutrestart)
02835                      *to = orig;
02836                   if (*to > 500) {
02837                      ring_one(qe, outgoing, &numbusies);
02838                      starttime = (long) time(NULL);
02839                   }
02840                }
02841             }
02842          }
02843       }
02844 
02845       /* If we received an event from the caller, deal with it. */
02846       if (winner == in) {
02847          f = ast_read(in);
02848          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02849             /* Got hung up */
02850             *to = -1;
02851             if (f) {
02852                if (f->data.uint32) {
02853                   in->hangupcause = f->data.uint32;
02854                }
02855                ast_frfree(f);
02856             }
02857             return NULL;
02858          }
02859          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02860             ast_verb(3, "User hit %c to disconnect call.\n", f->subclass);
02861             *to = 0;
02862             ast_frfree(f);
02863             return NULL;
02864          }
02865          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02866             ast_verb(3, "User pressed digit: %c\n", f->subclass);
02867             *to = 0;
02868             *digit = f->subclass;
02869             ast_frfree(f);
02870             return NULL;
02871          }
02872          ast_frfree(f);
02873       }
02874       if (!*to) {
02875          for (o = start; o; o = o->call_next)
02876             rna(orig, qe, o->interface, o->member->membername, 1);
02877       }
02878    }
02879 
02880 #ifdef HAVE_EPOLL
02881    for (epollo = outgoing; epollo; epollo = epollo->q_next) {
02882       if (epollo->chan)
02883          ast_poll_channel_del(in, epollo->chan);
02884    }
02885 #endif
02886 
02887    return peer;
02888 }

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

References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, queue_ent::expire, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, queue_ent::min_penalty, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, penalty_rule::time, ast_channel::uniqueid, update_qe_rule(), and valid_exit().

Referenced by queue_exec().

02977 {
02978    int res = 0;
02979 
02980    /* This is the holding pen for callers 2 through maxlen */
02981    for (;;) {
02982       enum queue_member_status status = QUEUE_NORMAL;
02983       int exit = 0;
02984 
02985       if (is_our_turn(qe))
02986          break;
02987 
02988       /* If we have timed out, break out */
02989       if (qe->expire && (time(NULL) >= qe->expire)) {
02990          *reason = QUEUE_TIMEOUT;
02991          break;
02992       }
02993 
02994       /* If we are going to exit due to a leavewhenempty condition, we should
02995        * actually attempt to keep the caller in the queue until we have
02996        * exhausted all penalty rules.
02997        */
02998       for (; !exit || qe->pr; update_qe_rule(qe)) {
02999          status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
03000   
03001          if (!qe->pr || status == QUEUE_NORMAL) {
03002             break;
03003          }
03004 
03005          /* leave the queue if no agents, if enabled */
03006          if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
03007                ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
03008                ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
03009             continue;
03010          } else {
03011             exit = 1;
03012          }
03013       }
03014 
03015       if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
03016          *reason = QUEUE_LEAVEEMPTY;
03017          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03018          leave_queue(qe);
03019          break;
03020       }
03021 
03022       /* leave the queue if no reachable agents, if enabled */
03023       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
03024          *reason = QUEUE_LEAVEUNAVAIL;
03025          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03026          leave_queue(qe);
03027          break;
03028       }
03029       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
03030          *reason = QUEUE_LEAVEUNAVAIL;
03031          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
03032          leave_queue(qe);
03033          break;
03034       }
03035 
03036       /* Make a position announcement, if enabled */
03037       if (qe->parent->announcefrequency &&
03038          (res = say_position(qe,ringing)))
03039          break;
03040 
03041       /* If we have timed out, break out */
03042       if (qe->expire && (time(NULL) >= qe->expire)) {
03043          *reason = QUEUE_TIMEOUT;
03044          break;
03045       }
03046 
03047       /* Make a periodic announcement, if enabled */
03048       if (qe->parent->periodicannouncefrequency &&
03049          (res = say_periodic_announcement(qe,ringing)))
03050          break;
03051       
03052       /* see if we need to move to the next penalty level for this queue */
03053       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03054          update_qe_rule(qe);
03055       }
03056 
03057       /* If we have timed out, break out */
03058       if (qe->expire && (time(NULL) >= qe->expire)) {
03059          *reason = QUEUE_TIMEOUT;
03060          break;
03061       }
03062       
03063       /* Wait a second before checking again */
03064       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03065          if (res > 0 && !valid_exit(qe, res))
03066             res = 0;
03067          else
03068             break;
03069       }
03070       
03071       /* If we have timed out, break out */
03072       if (qe->expire && (time(NULL) >= qe->expire)) {
03073          *reason = QUEUE_TIMEOUT;
03074          break;
03075       }
03076    }
03077 
03078    return res;
03079 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .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 = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 6868 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 154 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 213 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 215 of file app_queue.c.

char* app_aqm_synopsis = "Dynamically adds queue members" [static]

Definition at line 214 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 239 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 241 of file app_queue.c.

char* app_pqm_synopsis = "Pauses a queue member" [static]

Definition at line 240 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 272 of file app_queue.c.

char* app_ql_descrip [static]

Initial value:

"   QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n"
"Allows you to write your own events into the queue log\n"
"Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n"

Definition at line 274 of file app_queue.c.

char* app_ql_synopsis = "Writes to the queue_log" [static]

Definition at line 273 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 226 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 228 of file app_queue.c.

char* app_rqm_synopsis = "Dynamically removes queue members" [static]

Definition at line 227 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 257 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 259 of file app_queue.c.

char* app_upqm_synopsis = "Unpauses a queue member" [static]

Definition at line 258 of file app_queue.c.

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 6868 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 294 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 6740 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* descrip [static]

Definition at line 158 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 303 of file app_queue.c.

struct ast_taskprocessor* devicestate_tps [static]

Definition at line 138 of file app_queue.c.

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

enum queue_result id

Definition at line 320 of file app_queue.c.

Referenced by _sip_show_peers(), amixer_max(), idemodulator(), and setamixer().

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 297 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 280 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 6731 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 6737 of file app_queue.c.

int queue_keep_stats = 0 [static]

queues.conf [general] option

Definition at line 285 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 288 of file app_queue.c.

struct { ... } queue_results[]

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

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

struct ast_custom_function queuemembercount_dep [static]

Definition at line 5474 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 5461 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 5493 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberpenalty_function [static]

Definition at line 5502 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 547 of file app_queue.c.

Referenced by __queues_show(), add_to_queue(), compare_weight(), complete_queue(), complete_queue_remove_member(), find_queue_by_name_rt(), get_member_penalty(), interface_exists_global(), join_queue(), leave_queue(), load_module(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_queues(), remove_from_queue(), set_member_paused(), set_member_penalty(), try_calling(), unload_module(), update_queue(), update_realtime_members(), and update_status().

struct ast_custom_function queuevar_function [static]

Definition at line 5443 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

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

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 300 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* synopsis = "Queue a call for a call queue" [static]

Definition at line 156 of file app_queue.c.

char* text

Definition at line 321 of file app_queue.c.

Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), festival_exec(), handle_response(), iconv_read(), method_match(), parse_sip_options(), process_sdp(), reqprep(), sendtext_exec(), set_queue_result(), and sip_new().

int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 306 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 291 of file app_queue.c.


Generated on Wed Aug 18 22:34:00 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7