Wed Mar 3 22:41:59 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"
Include dependency graph for app_queue.c:

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 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>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 418 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 419 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 434 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 433 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 432 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 431 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 138 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

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

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 144 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 280 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_LOOSE   3

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

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

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

#define RECHECK   1

Recheck every second to see we we're at the top yet

Definition at line 140 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)

Member is not dynamic

Definition at line 150 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
#define RES_OUTOFMEMORY   (-2)

Enumeration Type Documentation

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

Definition at line 112 of file app_queue.c.

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3130 of file app_queue.c.

03130                            {
03131    CALLER,
03132    AGENT,
03133    TRANSFER
03134 };

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 654 of file app_queue.c.

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 306 of file app_queue.c.

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

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 331 of file app_queue.c.

00331                             {
00332    TIMEOUT_PRIORITY_APP,
00333    TIMEOUT_PRIORITY_CONF,
00334 };


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

References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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, F_AO2I_DONTLOCK, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_unref(), queues, member::realtime, call_queue::realtime, 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().

05722 {
05723    struct call_queue *q;
05724    struct ast_str *out = ast_str_alloca(240);
05725    int found = 0;
05726    time_t now = time(NULL);
05727    struct ao2_iterator queue_iter;
05728    struct ao2_iterator mem_iter;
05729 
05730    if (argc != 2 && argc != 3)
05731       return CLI_SHOWUSAGE;
05732 
05733    if (argc == 3) { /* specific queue */
05734       if ((q = load_realtime_queue(argv[2]))) {
05735          queue_unref(q);
05736       }
05737    } else if (ast_check_realtime("queues")) {
05738       /* This block is to find any queues which are defined in realtime but
05739        * which have not yet been added to the in-core container
05740        */
05741       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL);
05742       char *queuename;
05743       if (cfg) {
05744          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05745             if ((q = load_realtime_queue(queuename))) {
05746                queue_unref(q);
05747             }
05748          }
05749          ast_config_destroy(cfg);
05750       }
05751    }
05752 
05753    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05754    ao2_lock(queues);
05755    while ((q = ao2_iterator_next(&queue_iter))) {
05756       float sl;
05757       struct call_queue *realtime_queue = NULL;
05758 
05759       ao2_lock(q);
05760       /* This check is to make sure we don't print information for realtime
05761        * queues which have been deleted from realtime but which have not yet
05762        * been deleted from the in-core container
05763        */
05764       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05765          ao2_unlock(q);
05766          queue_unref(q);
05767          continue;
05768       } else if (q->realtime) {
05769          queue_unref(realtime_queue);
05770       }
05771       if (argc == 3 && strcasecmp(q->name, argv[2])) {
05772          ao2_unlock(q);
05773          queue_unref(q);
05774          continue;
05775       }
05776       found = 1;
05777 
05778       ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
05779       if (q->maxlen)
05780          ast_str_append(&out, 0, "%d", q->maxlen);
05781       else
05782          ast_str_append(&out, 0, "unlimited");
05783       sl = 0;
05784       if (q->callscompleted > 0)
05785          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05786       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05787          int2strat(q->strategy), q->holdtime, q->weight,
05788          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05789       do_print(s, fd, out->str);
05790       if (!ao2_container_count(q->members))
05791          do_print(s, fd, "   No Members");
05792       else {
05793          struct member *mem;
05794 
05795          do_print(s, fd, "   Members: ");
05796          mem_iter = ao2_iterator_init(q->members, 0);
05797          while ((mem = ao2_iterator_next(&mem_iter))) {
05798             ast_str_set(&out, 0, "      %s", mem->membername);
05799             if (strcasecmp(mem->membername, mem->interface)) {
05800                ast_str_append(&out, 0, " (%s)", mem->interface);
05801             }
05802             if (mem->penalty)
05803                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05804             ast_str_append(&out, 0, "%s%s%s (%s)",
05805                mem->dynamic ? " (dynamic)" : "",
05806                mem->realtime ? " (realtime)" : "",
05807                mem->paused ? " (paused)" : "",
05808                devstate2str(mem->status));
05809             if (mem->calls)
05810                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05811                   mem->calls, (long) (time(NULL) - mem->lastcall));
05812             else
05813                ast_str_append(&out, 0, " has taken no calls yet");
05814             do_print(s, fd, out->str);
05815             ao2_ref(mem, -1);
05816          }
05817       }
05818       if (!q->head)
05819          do_print(s, fd, "   No Callers");
05820       else {
05821          struct queue_ent *qe;
05822          int pos = 1;
05823 
05824          do_print(s, fd, "   Callers: ");
05825          for (qe = q->head; qe; qe = qe->next) {
05826             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
05827                pos++, qe->chan->name, (long) (now - qe->start) / 60,
05828                (long) (now - qe->start) % 60, qe->prio);
05829             do_print(s, fd, out->str);
05830          }
05831       }
05832       do_print(s, fd, ""); /* blank line between entries */
05833       ao2_unlock(q);
05834       queue_unref(q); /* Unref the iterator's reference */
05835    }
05836    ao2_unlock(queues);
05837    if (!found) {
05838       if (argc == 3)
05839          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05840       else
05841          ast_str_set(&out, 0, "No queues.");
05842       do_print(s, fd, out->str);
05843    }
05844    return CLI_SUCCESS;
05845 }

static void __reg_module ( void   )  [static]

Definition at line 6779 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 6779 of file app_queue.c.

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

Definition at line 962 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, and member_interface::interface.

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

00963 {
00964    struct member_interface *curint;
00965 
00966    AST_LIST_LOCK(&interfaces);
00967    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00968       if (!strcasecmp(curint->interface, interface))
00969          break;
00970    }
00971 
00972    if (curint) {
00973       AST_LIST_UNLOCK(&interfaces);
00974       return 0;
00975    }
00976 
00977    ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00978    
00979    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00980       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00981       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00982    }
00983    AST_LIST_UNLOCK(&interfaces);
00984 
00985    return 0;
00986 }

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

04192 {
04193    struct call_queue *q;
04194    struct member *new_member, *old_member;
04195    int res = RES_NOSUCHQUEUE;
04196 
04197    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
04198     * short-circuits if the queue is already in memory. */
04199    if (!(q = load_realtime_queue(queuename)))
04200       return res;
04201 
04202    ao2_lock(queues);
04203 
04204    ao2_lock(q);
04205    if ((old_member = interface_exists(q, interface)) == NULL) {
04206       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04207          add_to_interfaces(new_member->state_interface);
04208          new_member->dynamic = 1;
04209          ao2_link(q->members, new_member);
04210          q->membercount++;
04211          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04212             "Queue: %s\r\n"
04213             "Location: %s\r\n"
04214             "MemberName: %s\r\n"
04215             "Membership: %s\r\n"
04216             "Penalty: %d\r\n"
04217             "CallsTaken: %d\r\n"
04218             "LastCall: %d\r\n"
04219             "Status: %d\r\n"
04220             "Paused: %d\r\n",
04221             q->name, new_member->interface, new_member->membername,
04222             "dynamic",
04223             new_member->penalty, new_member->calls, (int) new_member->lastcall,
04224             new_member->status, new_member->paused);
04225          
04226          ao2_ref(new_member, -1);
04227          new_member = NULL;
04228 
04229          if (dump)
04230             dump_queue_members(q);
04231          
04232          res = RES_OKAY;
04233       } else {
04234          res = RES_OUTOFMEMORY;
04235       }
04236    } else {
04237       ao2_ref(old_member, -1);
04238       res = RES_EXISTS;
04239    }
04240    ao2_unlock(q);
04241    ao2_unlock(queues);
04242 
04243    return res;
04244 }

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

Definition at line 1436 of file app_queue.c.

References ao2_alloc, ao2_ref, ast_string_field_init, ast_string_field_set, and destroy_queue().

Referenced by find_queue_by_name_rt(), and reload_queues().

01437 {
01438    struct call_queue *q;
01439 
01440    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
01441       if (ast_string_field_init(q, 64)) {
01442          ao2_ref(q, -1);
01443          return NULL;
01444       }
01445       ast_string_field_set(q, name, queuename);
01446    }
01447    return q;
01448 }

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

AddQueueMember application.

Definition at line 4625 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(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

04626 {
04627    int res=-1;
04628    char *parse, *temppos = NULL;
04629    AST_DECLARE_APP_ARGS(args,
04630       AST_APP_ARG(queuename);
04631       AST_APP_ARG(interface);
04632       AST_APP_ARG(penalty);
04633       AST_APP_ARG(options);
04634       AST_APP_ARG(membername);
04635       AST_APP_ARG(state_interface);
04636    );
04637    int penalty = 0;
04638 
04639    if (ast_strlen_zero(data)) {
04640       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04641       return -1;
04642    }
04643 
04644    parse = ast_strdupa(data);
04645 
04646    AST_STANDARD_APP_ARGS(args, parse);
04647 
04648    if (ast_strlen_zero(args.interface)) {
04649       args.interface = ast_strdupa(chan->name);
04650       temppos = strrchr(args.interface, '-');
04651       if (temppos)
04652          *temppos = '\0';
04653    }
04654 
04655    if (!ast_strlen_zero(args.penalty)) {
04656       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04657          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04658          penalty = 0;
04659       }
04660    }
04661 
04662    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04663    case RES_OKAY:
04664       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04665       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04666       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04667       res = 0;
04668       break;
04669    case RES_EXISTS:
04670       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04671       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04672       res = 0;
04673       break;
04674    case RES_NOSUCHQUEUE:
04675       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04676       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04677       res = 0;
04678       break;
04679    case RES_OUTOFMEMORY:
04680       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04681       break;
04682    }
04683 
04684    return res;
04685 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

03235 {
03236    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03237 }

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

Referenced by try_calling().

03074 {
03075    if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03076       return -1;
03077 
03078    switch (q->strategy) {
03079    case QUEUE_STRATEGY_RINGALL:
03080       /* Everyone equal, except for penalty */
03081       tmp->metric = mem->penalty * 1000000;
03082       break;
03083    case QUEUE_STRATEGY_LINEAR:
03084       if (pos < qe->linpos) {
03085          tmp->metric = 1000 + pos;
03086       } else {
03087          if (pos > qe->linpos)
03088             /* Indicate there is another priority */
03089             qe->linwrapped = 1;
03090          tmp->metric = pos;
03091       }
03092       tmp->metric += mem->penalty * 1000000;
03093       break;
03094    case QUEUE_STRATEGY_RRMEMORY:
03095       if (pos < q->rrpos) {
03096          tmp->metric = 1000 + pos;
03097       } else {
03098          if (pos > q->rrpos)
03099             /* Indicate there is another priority */
03100             q->wrapped = 1;
03101          tmp->metric = pos;
03102       }
03103       tmp->metric += mem->penalty * 1000000;
03104       break;
03105    case QUEUE_STRATEGY_RANDOM:
03106       tmp->metric = ast_random() % 1000;
03107       tmp->metric += mem->penalty * 1000000;
03108       break;
03109    case QUEUE_STRATEGY_WRANDOM:
03110       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03111       break;
03112    case QUEUE_STRATEGY_FEWESTCALLS:
03113       tmp->metric = mem->calls;
03114       tmp->metric += mem->penalty * 1000000;
03115       break;
03116    case QUEUE_STRATEGY_LEASTRECENT:
03117       if (!mem->lastcall)
03118          tmp->metric = 0;
03119       else
03120          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03121       tmp->metric += mem->penalty * 1000000;
03122       break;
03123    default:
03124       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03125       break;
03126    }
03127    return 0;
03128 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1036 of file app_queue.c.

References ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, and AST_LIST_UNLOCK.

Referenced by unload_module().

01037 {
01038    struct member_interface *curint;
01039 
01040    AST_LIST_LOCK(&interfaces);
01041    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01042       ast_free(curint);
01043    AST_LIST_UNLOCK(&interfaces);
01044 }

static void clear_queue ( struct call_queue q  )  [static]

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

00954 {
00955    q->holdtime = 0;
00956    q->callscompleted = 0;
00957    q->callsabandoned = 0;
00958    q->callscompletedinsl = 0;
00959    q->wrapuptime = 0;
00960 }

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

Definition at line 2124 of file app_queue.c.

References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, call_queue::count, member::interface, call_queue::members, call_queue::name, num_available_members(), OBJ_POINTER, queue_unref(), queues, and call_queue::weight.

Referenced by ring_entry().

02125 {
02126    struct call_queue *q;
02127    struct member *mem;
02128    int found = 0;
02129    struct ao2_iterator queue_iter;
02130    
02131    /* q's lock and rq's lock already set by try_calling()
02132     * to solve deadlock */
02133    queue_iter = ao2_iterator_init(queues, 0);
02134    while ((q = ao2_iterator_next(&queue_iter))) {
02135       if (q == rq) { /* don't check myself, could deadlock */
02136          queue_unref(q);
02137          continue;
02138       }
02139       ao2_lock(q);
02140       if (q->count && q->members) {
02141          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02142             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02143             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02144                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);
02145                found = 1;
02146             }
02147             ao2_ref(mem, -1);
02148          }
02149       }
02150       ao2_unlock(q);
02151       queue_unref(q);
02152       if (found) {
02153          break;
02154       }
02155    }
02156    return found;
02157 }

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

Definition at line 5847 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ast_strdup, call_queue::name, queue_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().

05848 {
05849    struct call_queue *q;
05850    char *ret = NULL;
05851    int which = 0;
05852    int wordlen = strlen(word);
05853    struct ao2_iterator queue_iter;
05854 
05855    queue_iter = ao2_iterator_init(queues, 0);
05856    while ((q = ao2_iterator_next(&queue_iter))) {
05857       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05858          ret = ast_strdup(q->name);
05859          queue_unref(q);
05860          break;
05861       }
05862       queue_unref(q);
05863    }
05864 
05865    return ret;
05866 }

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

Definition at line 6224 of file app_queue.c.

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

Referenced by handle_queue_add_member().

06225 {
06226    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
06227    switch (pos) {
06228    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
06229       return NULL;
06230    case 4: /* only one possible match, "to" */
06231       return state == 0 ? ast_strdup("to") : NULL;
06232    case 5: /* <queue> */
06233       return complete_queue(line, word, pos, state);
06234    case 6: /* only one possible match, "penalty" */
06235       return state == 0 ? ast_strdup("penalty") : NULL;
06236    case 7:
06237       if (state < 100) {      /* 0-99 */
06238          char *num;
06239          if ((num = ast_malloc(3))) {
06240             sprintf(num, "%d", state);
06241          }
06242          return num;
06243       } else {
06244          return NULL;
06245       }
06246    case 8: /* only one possible match, "as" */
06247       return state == 0 ? ast_strdup("as") : NULL;
06248    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
06249       return NULL;
06250    default:
06251       return NULL;
06252    }
06253 }

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

Definition at line 6438 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

06439 {
06440    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
06441    switch (pos) {
06442    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
06443       return NULL;
06444    case 4:  /* only one possible match, "queue" */
06445       return state == 0 ? ast_strdup("queue") : NULL;
06446    case 5:  /* <queue> */
06447       return complete_queue(line, word, pos, state);
06448    case 6: /* "reason" */
06449       return state == 0 ? ast_strdup("reason") : NULL;
06450    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
06451       return NULL;
06452    default:
06453       return NULL;
06454    }
06455 }

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

Definition at line 6354 of file app_queue.c.

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

Referenced by handle_queue_remove_member().

06355 {
06356    int which = 0;
06357    struct call_queue *q;
06358    struct member *m;
06359    struct ao2_iterator queue_iter;
06360    struct ao2_iterator mem_iter;
06361    int wordlen = strlen(word);
06362 
06363    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
06364    if (pos > 5 || pos < 3)
06365       return NULL;
06366    if (pos == 4)   /* only one possible match, 'from' */
06367       return (state == 0 ? ast_strdup("from") : NULL);
06368 
06369    if (pos == 5)   /* No need to duplicate code */
06370       return complete_queue(line, word, pos, state);
06371 
06372    /* here is the case for 3, <member> */
06373    queue_iter = ao2_iterator_init(queues, 0);
06374    while ((q = ao2_iterator_next(&queue_iter))) {
06375       ao2_lock(q);
06376       mem_iter = ao2_iterator_init(q->members, 0);
06377       while ((m = ao2_iterator_next(&mem_iter))) {
06378          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06379             char *tmp;
06380             ao2_unlock(q);
06381             tmp = ast_strdup(m->interface);
06382             ao2_ref(m, -1);
06383             queue_unref(q);
06384             return tmp;
06385          }
06386          ao2_ref(m, -1);
06387       }
06388       ao2_unlock(q);
06389       queue_unref(q);
06390    }
06391 
06392    return NULL;
06393 }

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

Definition at line 6571 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

06572 {
06573    int which = 0;
06574    struct rule_list *rl_iter;
06575    int wordlen = strlen(word);
06576    char *ret = NULL;
06577    if (pos != 3) /* Wha? */ {
06578       return NULL;
06579    }
06580 
06581    AST_LIST_LOCK(&rule_lists);
06582    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06583       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06584          ret = ast_strdup(rl_iter->name);
06585          break;
06586       }
06587    }
06588    AST_LIST_UNLOCK(&rule_lists);
06589 
06590    return ret;
06591 }

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

Definition at line 6508 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

06509 {
06510    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
06511    switch (pos) {
06512    case 4:
06513       if (state == 0) {
06514          return ast_strdup("on");
06515       } else {
06516          return NULL;
06517       }
06518    case 6:
06519       if (state == 0) {
06520          return ast_strdup("in");
06521       } else {
06522          return NULL;
06523       }
06524    case 7:
06525       return complete_queue(line, word, pos, state);
06526    default:
06527       return NULL;
06528    }
06529 }

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

Definition at line 5868 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

05869 {
05870    if (pos == 2)
05871       return complete_queue(line, word, pos, state);
05872    return NULL;
05873 }

static int compress_char ( const char  c  )  [static]

Definition at line 851 of file app_queue.c.

Referenced by member_hash_fn().

00852 {
00853    if (c < 32)
00854       return 0;
00855    else if (c > 96)
00856       return c - 64;
00857    else
00858       return c - 32;
00859 }

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

Copy rule from global list into specified queue.

Definition at line 4722 of file app_queue.c.

References ast_calloc, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strlen_zero(), LOG_ERROR, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, queue_ent::qe_rules, rule_list::rules, and penalty_rule::time.

Referenced by join_queue().

04723 {
04724    struct penalty_rule *pr_iter;
04725    struct rule_list *rl_iter;
04726    const char *tmp = rulename;
04727    if (ast_strlen_zero(tmp)) {
04728       return;
04729    }
04730    AST_LIST_LOCK(&rule_lists);
04731    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04732       if (!strcasecmp(rl_iter->name, tmp))
04733          break;
04734    }
04735    if (rl_iter) {
04736       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04737          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04738          if (!new_pr) {
04739             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04740             AST_LIST_UNLOCK(&rule_lists);
04741             break;
04742          }
04743          new_pr->time = pr_iter->time;
04744          new_pr->max_value = pr_iter->max_value;
04745          new_pr->min_value = pr_iter->min_value;
04746          new_pr->max_relative = pr_iter->max_relative;
04747          new_pr->min_relative = pr_iter->min_relative;
04748          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04749       }
04750    }
04751    AST_LIST_UNLOCK(&rule_lists);
04752 }

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

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

Definition at line 826 of file app_queue.c.

References ao2_alloc, ast_copy_string(), ast_log(), ast_strlen_zero(), member::interface, LOG_WARNING, member::membername, member::paused, member::penalty, member::state_interface, and member::status.

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

00827 {
00828    struct member *cur;
00829    
00830    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00831       cur->penalty = penalty;
00832       cur->paused = paused;
00833       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00834       if (!ast_strlen_zero(state_interface))
00835          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00836       else
00837          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00838       if (!ast_strlen_zero(membername))
00839          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00840       else
00841          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00842       if (!strchr(cur->interface, '/'))
00843          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00844       cur->status = ast_device_state(cur->state_interface);
00845    }
00846 
00847    return cur;
00848 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

01423 {
01424    struct call_queue *q = obj;
01425    int i;
01426 
01427    free_members(q, 1);
01428    ast_string_field_free_memory(q);
01429    for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
01430       if (q->sound_periodicannounce[i])
01431          free(q->sound_periodicannounce[i]);
01432    }
01433    ao2_ref(q->members, -1);
01434 }

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

Definition at line 799 of file app_queue.c.

References ast_calloc, ast_event_get_ie_str(), ast_event_get_ie_uint(), AST_EVENT_IE_DEVICE, AST_EVENT_IE_STATE, ast_free, ast_log(), ast_strlen_zero(), ast_taskprocessor_push(), statechange::dev, handle_statechange(), LOG_ERROR, and statechange::state.

Referenced by load_module().

00800 {
00801    enum ast_device_state state;
00802    const char *device;
00803    struct statechange *sc;
00804    size_t datapsize;
00805 
00806    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00807    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00808 
00809    if (ast_strlen_zero(device)) {
00810       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
00811       return;
00812    }
00813    datapsize = sizeof(*sc) + strlen(device) + 1;
00814    if (!(sc = ast_calloc(1, datapsize))) {
00815       ast_log(LOG_ERROR, "failed to calloc a state change struct\n");
00816       return;
00817    }
00818    sc->state = state;
00819    strcpy(sc->dev, device);
00820    if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) {
00821       ast_free(sc);
00822    }
00823 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2160 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

02161 {
02162    o->stillgoing = 0;
02163    ast_hangup(o->chan);
02164    o->chan = NULL;
02165 }

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

References ast_cli(), and astman_append().

Referenced by __queues_show().

05708 {
05709    if (s)
05710       astman_append(s, "%s\r\n", str);
05711    else
05712       ast_cli(fd, "%s\n", str);
05713 }

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

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

04093 {
04094    struct member *cur_member;
04095    char value[PM_MAX_LEN];
04096    int value_len = 0;
04097    int res;
04098    struct ao2_iterator mem_iter;
04099 
04100    memset(value, 0, sizeof(value));
04101 
04102    if (!pm_queue)
04103       return;
04104 
04105    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04106    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04107       if (!cur_member->dynamic) {
04108          ao2_ref(cur_member, -1);
04109          continue;
04110       }
04111 
04112       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04113          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04114 
04115       ao2_ref(cur_member, -1);
04116 
04117       if (res != strlen(value + value_len)) {
04118          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04119          break;
04120       }
04121       value_len += res;
04122    }
04123    
04124    if (value_len && !cur_member) {
04125       if (ast_db_put(pm_family, pm_queue->name, value))
04126          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04127    } else
04128       /* Delete the entry if the queue is empty or there is an error */
04129       ast_db_del(pm_family, pm_queue->name);
04130 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 3281 of file app_queue.c.

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

Referenced by try_calling().

03282 {
03283    struct queue_end_bridge *qeb = data;
03284    struct call_queue *q = qeb->q;
03285    struct ast_channel *chan = qeb->chan;
03286 
03287    if (ao2_ref(qeb, -1) == 1) {
03288       ao2_lock(q);
03289       set_queue_variables(q, chan);
03290       ao2_unlock(q);
03291       /* This unrefs the reference we made in try_calling when we allocated qeb */
03292       queue_unref(q);
03293    }
03294 }

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

Definition at line 3274 of file app_queue.c.

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

Referenced by try_calling().

03275 {
03276    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03277    ao2_ref(qeb, +1);
03278    qeb->chan = originator;
03279 }

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

find the entry with the best metric, or NULL

Definition at line 2371 of file app_queue.c.

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

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

02372 {
02373    struct callattempt *best = NULL, *cur;
02374 
02375    for (cur = outgoing; cur; cur = cur->q_next) {
02376       if (cur->stillgoing &&              /* Not already done */
02377          !cur->chan &&              /* Isn't already going */
02378          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02379          best = cur;
02380       }
02381    }
02382 
02383    return best;
02384 }

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

Reload a single queue via realtime.

Check for statically defined queue first, check if deleted RT queue, check for new RT queue, if queue vars are not defined init them with defaults. reload RT queue vars, set RT queue members dead and reload them, return finished queue.

Return values:
the queue,
NULL if it doesn't exist.
Note:
Should be called with the "queues" container locked.

Note:
Hmm, can't seem to distinguish a DB failure from a not found condition... So we might delete an in-core queue in case of DB failure.

Definition at line 1460 of file app_queue.c.

References alloc_queue(), ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), ast_category_browse(), ast_copy_string(), ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), ast_variable_retrieve(), clear_queue(), member::dead, call_queue::dead, init_queue(), member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_unref(), queues, member::realtime, call_queue::realtime, remove_from_interfaces(), rt_handle_member_record(), S_OR, member::state_interface, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

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

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

Iterate through queue's member list and delete them.

Definition at line 1405 of file app_queue.c.

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

01406 {
01407    /* Free non-dynamic members */
01408    struct member *cur;
01409    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01410 
01411    while ((cur = ao2_iterator_next(&mem_iter))) {
01412       if (all || !cur->dynamic) {
01413          ao2_unlink(q->members, cur);
01414          remove_from_interfaces(cur->state_interface, 1);
01415          q->membercount--;
01416       }
01417       ao2_ref(cur, -1);
01418    }
01419 }

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

Definition at line 4369 of file app_queue.c.

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

Referenced by queue_function_memberpenalty_read().

04370 {
04371    int foundqueue = 0, penalty;
04372    struct call_queue *q, tmpq = {
04373       .name = queuename,   
04374    };
04375    struct member *mem;
04376    
04377    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04378       foundqueue = 1;
04379       ao2_lock(q);
04380       if ((mem = interface_exists(q, interface))) {
04381          penalty = mem->penalty;
04382          ao2_ref(mem, -1);
04383          ao2_unlock(q);
04384          queue_unref(q);
04385          return penalty;
04386       }
04387       ao2_unlock(q);
04388       queue_unref(q);
04389    }
04390 
04391    /* some useful debuging */
04392    if (foundqueue) 
04393       ast_log (LOG_ERROR, "Invalid queuename\n");
04394    else 
04395       ast_log (LOG_ERROR, "Invalid interface\n");
04396 
04397    return RESULT_FAILURE;
04398 }

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

References 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, QUEUE_NORMAL, and member::status.

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

00668 {
00669    struct member *member;
00670    struct ao2_iterator mem_iter;
00671    enum queue_member_status result = QUEUE_NO_MEMBERS;
00672 
00673    ao2_lock(q);
00674    mem_iter = ao2_iterator_init(q->members, 0);
00675    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00676       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
00677          continue;
00678 
00679       switch (member->status) {
00680       case AST_DEVICE_INVALID:
00681          /* nothing to do */
00682          break;
00683       case AST_DEVICE_UNAVAILABLE:
00684          if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS) 
00685             result = QUEUE_NO_REACHABLE_MEMBERS;
00686          break;
00687       default:
00688          if (member->paused) {
00689             result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
00690          } else {
00691             ao2_unlock(q);
00692             ao2_ref(member, -1);
00693             return QUEUE_NORMAL;
00694          }
00695          break;
00696       }
00697    }
00698 
00699    ao2_unlock(q);
00700    return result;
00701 }

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

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

06281 {
06282    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06283    int penalty;
06284 
06285    switch ( cmd ) {
06286    case CLI_INIT:
06287       e->command = "queue add member";
06288       e->usage =
06289          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"
06290          "       Add a channel to a queue with optionally:  a penalty, membername and a state_interface\n";
06291       return NULL;
06292    case CLI_GENERATE:
06293       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06294    }
06295 
06296    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06297       return CLI_SHOWUSAGE;
06298    } else if (strcmp(a->argv[4], "to")) {
06299       return CLI_SHOWUSAGE;
06300    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06301       return CLI_SHOWUSAGE;
06302    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06303       return CLI_SHOWUSAGE;
06304    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06305       return CLI_SHOWUSAGE;
06306    }
06307 
06308    queuename = a->argv[5];
06309    interface = a->argv[3];
06310    if (a->argc >= 8) {
06311       if (sscanf(a->argv[7], "%30d", &penalty) == 1) {
06312          if (penalty < 0) {
06313             ast_cli(a->fd, "Penalty must be >= 0\n");
06314             penalty = 0;
06315          }
06316       } else {
06317          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06318          penalty = 0;
06319       }
06320    } else {
06321       penalty = 0;
06322    }
06323 
06324    if (a->argc >= 10) {
06325       membername = a->argv[9];
06326    }
06327 
06328    if (a->argc >= 12) {
06329       state_interface = a->argv[11];
06330    }
06331 
06332    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06333    case RES_OKAY:
06334       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06335       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06336       return CLI_SUCCESS;
06337    case RES_EXISTS:
06338       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06339       return CLI_FAILURE;
06340    case RES_NOSUCHQUEUE:
06341       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06342       return CLI_FAILURE;
06343    case RES_OUTOFMEMORY:
06344       ast_cli(a->fd, "Out of memory\n");
06345       return CLI_FAILURE;
06346    case RES_NOT_DYNAMIC:
06347       ast_cli(a->fd, "Member not dynamic\n");
06348       return CLI_FAILURE;
06349    default:
06350       return CLI_FAILURE;
06351    }
06352 }

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

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

06458 {
06459    char *queuename, *interface, *reason;
06460    int paused;
06461 
06462    switch (cmd) {
06463    case CLI_INIT:
06464       e->command = "queue {pause|unpause} member";
06465       e->usage = 
06466          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06467          "  Pause or unpause a queue member. Not specifying a particular queue\n"
06468          "  will pause or unpause a member across all queues to which the member\n"
06469          "  belongs.\n";
06470       return NULL;
06471    case CLI_GENERATE:
06472       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06473    }
06474 
06475    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06476       return CLI_SHOWUSAGE;
06477    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06478       return CLI_SHOWUSAGE;
06479    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06480       return CLI_SHOWUSAGE;
06481    }
06482 
06483 
06484    interface = a->argv[3];
06485    queuename = a->argc >= 6 ? a->argv[5] : NULL;
06486    reason = a->argc == 8 ? a->argv[7] : NULL;
06487    paused = !strcasecmp(a->argv[1], "pause");
06488 
06489    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06490       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06491       if (!ast_strlen_zero(queuename))
06492          ast_cli(a->fd, " in queue '%s'", queuename);
06493       if (!ast_strlen_zero(reason))
06494          ast_cli(a->fd, " for reason '%s'", reason);
06495       ast_cli(a->fd, "\n");
06496       return CLI_SUCCESS;
06497    } else {
06498       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06499       if (!ast_strlen_zero(queuename))
06500          ast_cli(a->fd, " in queue '%s'", queuename);
06501       if (!ast_strlen_zero(reason))
06502          ast_cli(a->fd, " for reason '%s'", reason);
06503       ast_cli(a->fd, "\n");
06504       return CLI_FAILURE;
06505    }
06506 }

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

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

06396 {
06397    char *queuename, *interface;
06398 
06399    switch (cmd) {
06400    case CLI_INIT:
06401       e->command = "queue remove member";
06402       e->usage = 
06403          "Usage: queue remove member <channel> from <queue>\n"
06404          "       Remove a specific channel from a queue.\n";
06405       return NULL;
06406    case CLI_GENERATE:
06407       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06408    }
06409 
06410    if (a->argc != 6) {
06411       return CLI_SHOWUSAGE;
06412    } else if (strcmp(a->argv[4], "from")) {
06413       return CLI_SHOWUSAGE;
06414    }
06415 
06416    queuename = a->argv[5];
06417    interface = a->argv[3];
06418 
06419    switch (remove_from_queue(queuename, interface)) {
06420    case RES_OKAY:
06421       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06422       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06423       return CLI_SUCCESS;
06424    case RES_EXISTS:
06425       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06426       return CLI_FAILURE;
06427    case RES_NOSUCHQUEUE:
06428       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06429       return CLI_FAILURE;
06430    case RES_OUTOFMEMORY:
06431       ast_cli(a->fd, "Out of memory\n");
06432       return CLI_FAILURE;
06433    default:
06434       return CLI_FAILURE;
06435    }
06436 }

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

Definition at line 6627 of file app_queue.c.

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

06628 {
06629    switch (cmd) {
06630       case CLI_INIT:
06631          e->command = "queue rules reload";
06632          e->usage = 
06633             "Usage: queue rules reload\n"
06634             "  Reloads rules defined in queuerules.conf\n";
06635          return NULL;
06636       case CLI_GENERATE:
06637          return NULL;
06638    }
06639    reload_queue_rules(1);
06640    return CLI_SUCCESS;
06641 }

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

Definition at line 6593 of file app_queue.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_rule_show(), ast_cli_args::fd, ast_cli_args::line, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, ast_cli_args::n, rule_list::name, ast_cli_args::pos, rule_list::rules, penalty_rule::time, ast_cli_entry::usage, and ast_cli_args::word.

06594 {
06595    char *rule;
06596    struct rule_list *rl_iter;
06597    struct penalty_rule *pr_iter;
06598    switch (cmd) {
06599    case CLI_INIT:
06600       e->command = "queue rules show";
06601       e->usage =
06602       "Usage: queue rules show [rulename]\n"
06603       "  Show the list of rules associated with rulename. If no\n"
06604       "  rulename is specified, list all rules defined in queuerules.conf\n";
06605       return NULL;
06606    case CLI_GENERATE:
06607       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06608    }
06609 
06610    if (a->argc != 3 && a->argc != 4)
06611       return CLI_SHOWUSAGE;
06612 
06613    rule = a->argc == 4 ? a->argv[3] : "";
06614    AST_LIST_LOCK(&rule_lists);
06615    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06616       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06617          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06618          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06619             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);
06620          }
06621       }
06622    }
06623    AST_LIST_UNLOCK(&rule_lists);
06624    return CLI_SUCCESS; 
06625 }

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

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

06532 {
06533    char *queuename = NULL, *interface;
06534    int penalty = 0;
06535 
06536    switch (cmd) {
06537    case CLI_INIT:
06538       e->command = "queue set penalty";
06539       e->usage = 
06540       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06541       "  Set a member's penalty in the queue specified. If no queue is specified\n"
06542       "  then that interface's penalty is set in all queues to which that interface is a member\n";
06543       return NULL;
06544    case CLI_GENERATE:
06545       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06546    }
06547 
06548    if (a->argc != 6 && a->argc != 8) {
06549       return CLI_SHOWUSAGE;
06550    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06551       return CLI_SHOWUSAGE;
06552    }
06553 
06554    if (a->argc == 8)
06555       queuename = a->argv[7];
06556    interface = a->argv[5];
06557    penalty = atoi(a->argv[3]);
06558 
06559    switch (set_member_penalty(queuename, interface, penalty)) {
06560    case RESULT_SUCCESS:
06561       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06562       return CLI_SUCCESS;
06563    case RESULT_FAILURE:
06564       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06565       return CLI_FAILURE;
06566    default:
06567       return CLI_FAILURE;
06568    }
06569 }

static int handle_statechange ( void *  datap  )  [static]

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

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

Referenced by device_state_cb().

00768 {
00769    struct member_interface *curint;
00770    struct statechange *sc = datap;
00771    char interface[80];
00772 
00773    AST_LIST_LOCK(&interfaces);
00774    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00775       char *slash_pos;
00776       ast_copy_string(interface, curint->interface, sizeof(interface));
00777       if ((slash_pos = strchr(interface, '/')))
00778          if ((slash_pos = strchr(slash_pos + 1, '/')))
00779             *slash_pos = '\0';
00780 
00781       if (!strcasecmp(interface, sc->dev))
00782          break;
00783    }
00784    AST_LIST_UNLOCK(&interfaces);
00785 
00786    if (!curint) {
00787       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));
00788       ast_free(sc);
00789       return 0;
00790    }
00791 
00792    ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00793 
00794    update_status(sc->dev, sc->state);
00795    ast_free(sc);
00796    return 0;
00797 }

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

Hang up a list of outgoing calls.

Definition at line 2058 of file app_queue.c.

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

Referenced by try_calling().

02059 {
02060    struct callattempt *oo;
02061 
02062    while (outgoing) {
02063       /* Hangup any existing lines we have open */
02064       if (outgoing->chan && (outgoing->chan != exception))
02065          ast_hangup(outgoing->chan);
02066       oo = outgoing;
02067       outgoing = outgoing->q_next;
02068       if (oo->member)
02069          ao2_ref(oo->member, -1);
02070       ast_free(oo);
02071    }
02072 }

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 883 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, 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 find_queue_by_name_rt(), and reload_queues().

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

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

References call_queue::head, queue_ent::next, and queue_ent::parent.

Referenced by join_queue().

00636 {
00637    struct queue_ent *cur;
00638 
00639    if (!q || !new)
00640       return;
00641    if (prev) {
00642       cur = prev->next;
00643       prev->next = new;
00644    } else {
00645       cur = q->head;
00646       q->head = new;
00647    }
00648    new->next = cur;
00649    new->parent = q;
00650    new->pos = ++(*pos);
00651    new->opos = *pos;
00652 }

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

References ast_calloc, ast_free, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_strdupa, ast_strlen_zero(), LOG_ERROR, LOG_WARNING, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, rule_list::rules, and penalty_rule::time.

Referenced by reload_queue_rules().

01056 {
01057    char *timestr, *maxstr, *minstr, *contentdup;
01058    struct penalty_rule *rule = NULL, *rule_iter;
01059    struct rule_list *rl_iter;
01060    int penaltychangetime, inserted = 0;
01061 
01062    if (!(rule = ast_calloc(1, sizeof(*rule)))) {
01063       ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum);
01064       return -1;
01065    }
01066 
01067    contentdup = ast_strdupa(content);
01068    
01069    if (!(maxstr = strchr(contentdup, ','))) {
01070       ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum);
01071       ast_free(rule);
01072       return -1;
01073    }
01074 
01075    *maxstr++ = '\0';
01076    timestr = contentdup;
01077 
01078    if ((penaltychangetime = atoi(timestr)) < 0) {
01079       ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum);
01080       ast_free(rule);
01081       return -1;
01082    }
01083 
01084    rule->time = penaltychangetime;
01085 
01086    if ((minstr = strchr(maxstr,',')))
01087       *minstr++ = '\0';
01088    
01089    /* The last check will evaluate true if either no penalty change is indicated for a given rule
01090     * OR if a min penalty change is indicated but no max penalty change is */
01091    if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') {
01092       rule->max_relative = 1;
01093    }
01094 
01095    rule->max_value = atoi(maxstr);
01096 
01097    if (!ast_strlen_zero(minstr)) {
01098       if (*minstr == '+' || *minstr == '-')
01099          rule->min_relative = 1;
01100       rule->min_value = atoi(minstr);
01101    } else /*there was no minimum specified, so assume this means no change*/
01102       rule->min_relative = 1;
01103 
01104    /*We have the rule made, now we need to insert it where it belongs*/
01105    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){
01106       if (strcasecmp(rl_iter->name, list_name))
01107          continue;
01108 
01109       AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) {
01110          if (rule->time < rule_iter->time) {
01111             AST_LIST_INSERT_BEFORE_CURRENT(rule, list);
01112             inserted = 1;
01113             break;
01114          }
01115       }
01116       AST_LIST_TRAVERSE_SAFE_END;
01117    
01118       if (!inserted) {
01119          AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list);
01120       }
01121    }
01122 
01123    return 0;
01124 }

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

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

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

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

Definition at line 4069 of file app_queue.c.

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

04070 {
04071    struct member *mem;
04072    struct ao2_iterator mem_iter;
04073 
04074    if (!q)
04075       return NULL;
04076 
04077    mem_iter = ao2_iterator_init(q->members, 0);
04078    while ((mem = ao2_iterator_next(&mem_iter))) {
04079       if (!strcasecmp(interface, mem->interface))
04080          return mem;
04081       ao2_ref(mem, -1);
04082    }
04083 
04084    return NULL;
04085 }

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

Definition at line 988 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_copy_string(), F_AO2I_DONTLOCK, member::interface, call_queue::members, queue_unref(), queues, and member::state_interface.

Referenced by remove_from_interfaces().

00989 {
00990    struct call_queue *q;
00991    struct member *mem, tmpmem;
00992    struct ao2_iterator queue_iter, mem_iter;
00993    int ret = 0;
00994 
00995    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00996    queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK);
00997    while ((q = ao2_iterator_next(&queue_iter))) {
00998       ao2_lock(q);
00999       mem_iter = ao2_iterator_init(q->members, 0);
01000       while ((mem = ao2_iterator_next(&mem_iter))) { 
01001          if (!strcasecmp(mem->state_interface, interface)) {
01002             ao2_ref(mem, -1);
01003             ret = 1;
01004             break;
01005          }
01006       }
01007       ao2_unlock(q);
01008       queue_unref(q);
01009    }
01010 
01011    return ret;
01012 }

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

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

Referenced by queue_exec(), and wait_our_turn().

02850 {
02851    struct queue_ent *ch;
02852    int res;
02853    int avl;
02854    int idx = 0;
02855    /* This needs a lock. How many members are available to be served? */
02856    ao2_lock(qe->parent);
02857 
02858    avl = num_available_members(qe->parent);
02859 
02860    ch = qe->parent->head;
02861 
02862    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02863 
02864    while ((idx < avl) && (ch) && (ch != qe)) {
02865       if (!ch->pending)
02866          idx++;
02867       ch = ch->next;       
02868    }
02869 
02870    ao2_unlock(qe->parent);
02871 
02872    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02873    if (ch && idx < avl) {
02874       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02875       res = 1;
02876    } else {
02877       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02878       res = 0;
02879    }
02880 
02881    return res;
02882 }

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

Definition at line 1701 of file app_queue.c.

References call_queue::announce, 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, call_queue::context, 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, call_queue::moh, queue_ent::moh, call_queue::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, and update_qe_rule().

Referenced by queue_exec().

01702 {
01703    struct call_queue *q;
01704    struct queue_ent *cur, *prev = NULL;
01705    int res = -1;
01706    int pos = 0;
01707    int inserted = 0;
01708    enum queue_member_status status;
01709    int exit = 0;
01710 
01711    if (!(q = load_realtime_queue(queuename)))
01712       return res;
01713 
01714    ao2_lock(queues);
01715    ao2_lock(q);
01716 
01717    copy_rules(qe, S_OR(overriding_rule, q->defaultrule));
01718    qe->pr = AST_LIST_FIRST(&qe->qe_rules);
01719 
01720    /* This is our one */
01721    while (!exit) {
01722       status = get_member_status(q, qe->max_penalty, qe->min_penalty);
01723       if (!q->joinempty && (status == QUEUE_NO_MEMBERS))
01724          *reason = QUEUE_JOINEMPTY;
01725       else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01726          *reason = QUEUE_JOINUNAVAIL;
01727       else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS))
01728          *reason = QUEUE_JOINUNAVAIL;
01729       else if (q->maxlen && (q->count >= q->maxlen))
01730          *reason = QUEUE_FULL;
01731       else {
01732          /* There's space for us, put us at the right position inside
01733           * the queue.
01734           * Take into account the priority of the calling user */
01735          inserted = 0;
01736          prev = NULL;
01737          cur = q->head;
01738          while (cur) {
01739             /* We have higher priority than the current user, enter
01740              * before him, after all the other users with priority
01741              * higher or equal to our priority. */
01742             if ((!inserted) && (qe->prio > cur->prio)) {
01743                insert_entry(q, prev, qe, &pos);
01744                inserted = 1;
01745             }
01746             cur->pos = ++pos;
01747             prev = cur;
01748             cur = cur->next;
01749          }
01750          /* No luck, join at the end of the queue */
01751          if (!inserted)
01752             insert_entry(q, prev, qe, &pos);
01753          ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01754          ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01755          ast_copy_string(qe->context, q->context, sizeof(qe->context));
01756          q->count++;
01757          res = 0;
01758          manager_event(EVENT_FLAG_CALL, "Join",
01759             "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01760             qe->chan->name,
01761             S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01762             S_OR(qe->chan->cid.cid_name, "unknown"),
01763             q->name, qe->pos, q->count, qe->chan->uniqueid );
01764          ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01765       }
01766       if (!exit && qe->pr && res) {
01767          /* We failed to join the queue, but perhaps we can join if we move
01768           * to the next defined penalty rule
01769           */
01770          update_qe_rule(qe);
01771       } else {
01772          exit = 1;
01773       }
01774    }
01775    ao2_unlock(q);
01776    ao2_unlock(queues);
01777 
01778    return res;
01779 }

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

References ao2_lock(), ao2_unlink, 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, manager_event, call_queue::name, queue_ent::next, queue_ent::parent, queue_ent::pos, queue_ent::qe_rules, queue_ref(), queue_unref(), queues, call_queue::realtime, SENTINEL, and var.

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

02002 {
02003    struct call_queue *q;
02004    struct queue_ent *current, *prev = NULL;
02005    struct penalty_rule *pr_iter;
02006    int pos = 0;
02007 
02008    if (!(q = qe->parent))
02009       return;
02010    queue_ref(q);
02011    ao2_lock(q);
02012 
02013    prev = NULL;
02014    for (current = q->head; current; current = current->next) {
02015       if (current == qe) {
02016          q->count--;
02017 
02018          /* Take us out of the queue */
02019          manager_event(EVENT_FLAG_CALL, "Leave",
02020             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02021             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
02022          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02023          /* Take us out of the queue */
02024          if (prev)
02025             prev->next = current->next;
02026          else
02027             q->head = current->next;
02028          /* Free penalty rules */
02029          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02030             ast_free(pr_iter);
02031       } else {
02032          /* Renumber the people after us in the queue based on a new count */
02033          current->pos = ++pos;
02034          prev = current;
02035       }
02036    }
02037    ao2_unlock(q);
02038 
02039    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02040    if (q->realtime) {
02041       struct ast_variable *var;
02042       if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) {
02043          q->dead = 1;
02044       } else {
02045          ast_variables_destroy(var);
02046       }
02047    }
02048 
02049    if (q->dead) { 
02050       /* It's dead and nobody is in it, so kill it */
02051       ao2_unlink(queues, q);
02052    }
02053    /* unref the explicit ref earlier in the function */
02054    queue_unref(q);
02055 }

static int load_module ( void   )  [static]

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

06714 {
06715    int res;
06716    struct ast_context *con;
06717 
06718    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06719 
06720    if (!reload_queues(0))
06721       return AST_MODULE_LOAD_DECLINE;
06722 
06723    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06724    if (!con)
06725       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06726    else
06727       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06728 
06729    if (queue_persistent_members)
06730       reload_queue_members();
06731 
06732    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06733    res = ast_register_application(app, queue_exec, synopsis, descrip);
06734    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06735    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06736    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06737    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06738    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06739    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06740    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06741    res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06742    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06743    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06744    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06745    res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06746    res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 
06747    res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06748    res |= ast_custom_function_register(&queuevar_function);
06749    res |= ast_custom_function_register(&queuemembercount_function);
06750    res |= ast_custom_function_register(&queuemembercount_dep);
06751    res |= ast_custom_function_register(&queuememberlist_function);
06752    res |= ast_custom_function_register(&queuewaitingcount_function);
06753    res |= ast_custom_function_register(&queuememberpenalty_function);
06754 
06755    if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) {
06756       ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n");
06757    }
06758 
06759    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) {
06760       res = -1;
06761    }
06762 
06763    ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL);
06764 
06765    return res ? AST_MODULE_LOAD_DECLINE : 0;
06766 }

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

Note:
Load from realtime before taking the "queues" container lock, to avoid blocking all queue operations while waiting for the DB.

This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 1593 of file app_queue.c.

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

01594 {
01595    struct ast_variable *queue_vars;
01596    struct ast_config *member_config = NULL;
01597    struct call_queue *q = NULL, tmpq = {
01598       .name = queuename,   
01599    };
01600 
01601    /* Find the queue in the in-core list first. */
01602    q = ao2_find(queues, &tmpq, OBJ_POINTER);
01603 
01604    if (!q || q->realtime) {
01605       /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all
01606          queue operations while waiting for the DB.
01607 
01608          This will be two separate database transactions, so we might
01609          see queue parameters as they were before another process
01610          changed the queue and member list as it was after the change.
01611          Thus we might see an empty member list when a queue is
01612          deleted. In practise, this is unlikely to cause a problem. */
01613 
01614       queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL);
01615       if (queue_vars) {
01616          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL);
01617          if (!member_config) {
01618             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01619             ast_variables_destroy(queue_vars);
01620             return NULL;
01621          }
01622       }
01623 
01624       ao2_lock(queues);
01625       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01626       if (member_config)
01627          ast_config_destroy(member_config);
01628       if (queue_vars)
01629          ast_variables_destroy(queue_vars);
01630       ao2_unlock(queues);
01631 
01632    } else {
01633       update_realtime_members(q);
01634    }
01635    return q;
01636 }

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

Definition at line 6094 of file app_queue.c.

References add_to_queue(), ast_queue_log(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

06095 {
06096    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06097    int paused, penalty = 0;
06098 
06099    queuename = astman_get_header(m, "Queue");
06100    interface = astman_get_header(m, "Interface");
06101    penalty_s = astman_get_header(m, "Penalty");
06102    paused_s = astman_get_header(m, "Paused");
06103    membername = astman_get_header(m, "MemberName");
06104    state_interface = astman_get_header(m, "StateInterface");
06105 
06106    if (ast_strlen_zero(queuename)) {
06107       astman_send_error(s, m, "'Queue' not specified.");
06108       return 0;
06109    }
06110 
06111    if (ast_strlen_zero(interface)) {
06112       astman_send_error(s, m, "'Interface' not specified.");
06113       return 0;
06114    }
06115 
06116    if (ast_strlen_zero(penalty_s))
06117       penalty = 0;
06118    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
06119       penalty = 0;
06120 
06121    if (ast_strlen_zero(paused_s))
06122       paused = 0;
06123    else
06124       paused = abs(ast_true(paused_s));
06125 
06126    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06127    case RES_OKAY:
06128       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06129       astman_send_ack(s, m, "Added interface to queue");
06130       break;
06131    case RES_EXISTS:
06132       astman_send_error(s, m, "Unable to add interface: Already there");
06133       break;
06134    case RES_NOSUCHQUEUE:
06135       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06136       break;
06137    case RES_OUTOFMEMORY:
06138       astman_send_error(s, m, "Out of memory");
06139       break;
06140    }
06141 
06142    return 0;
06143 }

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

Definition at line 6179 of file app_queue.c.

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

Referenced by load_module().

06180 {
06181    const char *queuename, *interface, *paused_s, *reason;
06182    int paused;
06183 
06184    interface = astman_get_header(m, "Interface");
06185    paused_s = astman_get_header(m, "Paused");
06186    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
06187    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
06188 
06189    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06190       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06191       return 0;
06192    }
06193 
06194    paused = abs(ast_true(paused_s));
06195 
06196    if (set_member_paused(queuename, interface, reason, paused))
06197       astman_send_error(s, m, "Interface not found");
06198    else
06199       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06200    return 0;
06201 }

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

Definition at line 6203 of file app_queue.c.

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

Referenced by load_module().

06204 {
06205    const char *queuename, *event, *message, *interface, *uniqueid;
06206 
06207    queuename = astman_get_header(m, "Queue");
06208    uniqueid = astman_get_header(m, "UniqueId");
06209    interface = astman_get_header(m, "Interface");
06210    event = astman_get_header(m, "Event");
06211    message = astman_get_header(m, "Message");
06212 
06213    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06214       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06215       return 0;
06216    }
06217 
06218    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06219    astman_send_ack(s, m, "Event added successfully");
06220 
06221    return 0;
06222 }

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

Definition at line 6255 of file app_queue.c.

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

Referenced by load_module().

06256 {
06257    const char *queuename, *interface, *penalty_s;
06258    int penalty;
06259 
06260    interface = astman_get_header(m, "Interface");
06261    penalty_s = astman_get_header(m, "Penalty");
06262    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
06263    queuename = astman_get_header(m, "Queue");
06264 
06265    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06266       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06267       return 0;
06268    }
06269  
06270    penalty = atoi(penalty_s);
06271 
06272    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06273       astman_send_error(s, m, "Invalid interface, queuename or penalty");
06274    else
06275       astman_send_ack(s, m, "Interface penalty set successfully");
06276 
06277    return 0;
06278 }

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

Definition at line 5904 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, RESULT_SUCCESS, rule_list::rules, and penalty_rule::time.

Referenced by load_module().

05905 {
05906    const char *rule = astman_get_header(m, "Rule");
05907    struct rule_list *rl_iter;
05908    struct penalty_rule *pr_iter;
05909 
05910    AST_LIST_LOCK(&rule_lists);
05911    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05912       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05913          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05914          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05915             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 );
05916          }
05917          if (!ast_strlen_zero(rule))
05918             break;
05919       }
05920    }
05921    AST_LIST_UNLOCK(&rule_lists);
05922 
05923    astman_append(s, "\r\n\r\n");
05924 
05925    return RESULT_SUCCESS;
05926 }

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

Definition at line 5894 of file app_queue.c.

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

Referenced by load_module().

05895 {
05896    char *a[] = { "queue", "show" };
05897 
05898    __queues_show(s, -1, 2, a);
05899    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05900 
05901    return RESULT_SUCCESS;
05902 }

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

Queue status info via AMI.

Definition at line 6001 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_unref(), queues, RESULT_SUCCESS, S_OR, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by load_module().

06002 {
06003    time_t now;
06004    int pos;
06005    const char *id = astman_get_header(m,"ActionID");
06006    const char *queuefilter = astman_get_header(m,"Queue");
06007    const char *memberfilter = astman_get_header(m,"Member");
06008    char idText[256] = "";
06009    struct call_queue *q;
06010    struct queue_ent *qe;
06011    float sl = 0;
06012    struct member *mem;
06013    struct ao2_iterator queue_iter;
06014    struct ao2_iterator mem_iter;
06015 
06016    astman_send_ack(s, m, "Queue status will follow");
06017    time(&now);
06018    if (!ast_strlen_zero(id))
06019       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
06020 
06021    queue_iter = ao2_iterator_init(queues, 0);
06022    while ((q = ao2_iterator_next(&queue_iter))) {
06023       ao2_lock(q);
06024 
06025       /* List queue properties */
06026       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
06027          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
06028          astman_append(s, "Event: QueueParams\r\n"
06029             "Queue: %s\r\n"
06030             "Max: %d\r\n"
06031             "Strategy: %s\r\n"
06032             "Calls: %d\r\n"
06033             "Holdtime: %d\r\n"
06034             "Completed: %d\r\n"
06035             "Abandoned: %d\r\n"
06036             "ServiceLevel: %d\r\n"
06037             "ServicelevelPerf: %2.1f\r\n"
06038             "Weight: %d\r\n"
06039             "%s"
06040             "\r\n",
06041             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
06042             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
06043          /* List Queue Members */
06044          mem_iter = ao2_iterator_init(q->members, 0);
06045          while ((mem = ao2_iterator_next(&mem_iter))) {
06046             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
06047                astman_append(s, "Event: QueueMember\r\n"
06048                   "Queue: %s\r\n"
06049                   "Name: %s\r\n"
06050                   "Location: %s\r\n"
06051                   "Membership: %s\r\n"
06052                   "Penalty: %d\r\n"
06053                   "CallsTaken: %d\r\n"
06054                   "LastCall: %d\r\n"
06055                   "Status: %d\r\n"
06056                   "Paused: %d\r\n"
06057                   "%s"
06058                   "\r\n",
06059                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
06060                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
06061             }
06062             ao2_ref(mem, -1);
06063          }
06064          /* List Queue Entries */
06065          pos = 1;
06066          for (qe = q->head; qe; qe = qe->next) {
06067             astman_append(s, "Event: QueueEntry\r\n"
06068                "Queue: %s\r\n"
06069                "Position: %d\r\n"
06070                "Channel: %s\r\n"
06071                "CallerIDNum: %s\r\n"
06072                "CallerIDName: %s\r\n"
06073                "Wait: %ld\r\n"
06074                "%s"
06075                "\r\n",
06076                q->name, pos++, qe->chan->name,
06077                S_OR(qe->chan->cid.cid_num, "unknown"),
06078                S_OR(qe->chan->cid.cid_name, "unknown"),
06079                (long) (now - qe->start), idText);
06080          }
06081       }
06082       ao2_unlock(q);
06083       queue_unref(q);
06084    }
06085 
06086    astman_append(s,
06087       "Event: QueueStatusComplete\r\n"
06088       "%s"
06089       "\r\n",idText);
06090 
06091    return RESULT_SUCCESS;
06092 }

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

Summary of queue info via the AMI.

Definition at line 5929 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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_unref(), queues, RESULT_SUCCESS, queue_ent::start, and member::status.

Referenced by load_module().

05930 {
05931    time_t now;
05932    int qmemcount = 0;
05933    int qmemavail = 0;
05934    int qchancount = 0;
05935    int qlongestholdtime = 0;
05936    const char *id = astman_get_header(m, "ActionID");
05937    const char *queuefilter = astman_get_header(m, "Queue");
05938    char idText[256] = "";
05939    struct call_queue *q;
05940    struct queue_ent *qe;
05941    struct member *mem;
05942    struct ao2_iterator queue_iter;
05943    struct ao2_iterator mem_iter;
05944 
05945    astman_send_ack(s, m, "Queue summary will follow");
05946    time(&now);
05947    if (!ast_strlen_zero(id))
05948       snprintf(idText, 256, "ActionID: %s\r\n", id);
05949    queue_iter = ao2_iterator_init(queues, 0);
05950    while ((q = ao2_iterator_next(&queue_iter))) {
05951       ao2_lock(q);
05952 
05953       /* List queue properties */
05954       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05955          /* Reset the necessary local variables if no queuefilter is set*/
05956          qmemcount = 0;
05957          qmemavail = 0;
05958          qchancount = 0;
05959          qlongestholdtime = 0;
05960 
05961          /* List Queue Members */
05962          mem_iter = ao2_iterator_init(q->members, 0);
05963          while ((mem = ao2_iterator_next(&mem_iter))) {
05964             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
05965                ++qmemcount;
05966                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
05967                   ++qmemavail;
05968                }
05969             }
05970             ao2_ref(mem, -1);
05971          }
05972          for (qe = q->head; qe; qe = qe->next) {
05973             if ((now - qe->start) > qlongestholdtime) {
05974                qlongestholdtime = now - qe->start;
05975             }
05976             ++qchancount;
05977          }
05978          astman_append(s, "Event: QueueSummary\r\n"
05979             "Queue: %s\r\n"
05980             "LoggedIn: %d\r\n"
05981             "Available: %d\r\n"
05982             "Callers: %d\r\n" 
05983             "HoldTime: %d\r\n"
05984             "LongestHoldTime: %d\r\n"
05985             "%s"
05986             "\r\n",
05987             q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
05988       }
05989       ao2_unlock(q);
05990       queue_unref(q);
05991    }
05992    astman_append(s,
05993       "Event: QueueSummaryComplete\r\n"
05994       "%s"
05995       "\r\n", idText);
05996 
05997    return RESULT_SUCCESS;
05998 }

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

Definition at line 6145 of file app_queue.c.

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

Referenced by load_module().

06146 {
06147    const char *queuename, *interface;
06148 
06149    queuename = astman_get_header(m, "Queue");
06150    interface = astman_get_header(m, "Interface");
06151 
06152    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06153       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06154       return 0;
06155    }
06156 
06157    switch (remove_from_queue(queuename, interface)) {
06158    case RES_OKAY:
06159       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06160       astman_send_ack(s, m, "Removed interface from queue");
06161       break;
06162    case RES_EXISTS:
06163       astman_send_error(s, m, "Unable to remove interface: Not there");
06164       break;
06165    case RES_NOSUCHQUEUE:
06166       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06167       break;
06168    case RES_OUTOFMEMORY:
06169       astman_send_error(s, m, "Out of memory");
06170       break;
06171    case RES_NOT_DYNAMIC:
06172       astman_send_error(s, m, "Member not dynamic");
06173       break;
06174    }
06175 
06176    return 0;
06177 }

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

Definition at line 873 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

00874 {
00875    struct member *mem1 = obj1, *mem2 = obj2;
00876    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00877 }

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

Definition at line 861 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00862 {
00863    const struct member *mem = obj;
00864    const char *chname = strchr(mem->interface, '/');
00865    int ret = 0, i;
00866    if (!chname)
00867       chname = mem->interface;
00868    for (i = 0; i < 5 && chname[i]; i++)
00869       ret += compress_char(chname[i]) << (i * 6);
00870    return ret;
00871 }

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

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

02083 {
02084    struct member *mem;
02085    int avl = 0;
02086    struct ao2_iterator mem_iter;
02087 
02088    mem_iter = ao2_iterator_init(q->members, 0);
02089    while ((mem = ao2_iterator_next(&mem_iter))) {
02090       switch (mem->status) {
02091       case AST_DEVICE_INUSE:
02092          if (!q->ringinuse)
02093             break;
02094          /* else fall through */
02095       case AST_DEVICE_NOT_INUSE:
02096       case AST_DEVICE_UNKNOWN:
02097          if (!mem->paused) {
02098             avl++;
02099          }
02100          break;
02101       }
02102       ao2_ref(mem, -1);
02103 
02104       /* If autofill is not enabled or if the queue's strategy is ringall, then
02105        * we really don't care about the number of available members so much as we
02106        * do that there is at least one available.
02107        *
02108        * In fact, we purposely will return from this function stating that only
02109        * one member is available if either of those conditions hold. That way,
02110        * functions which determine what action to take based on the number of available
02111        * members will operate properly. The reasoning is that even if multiple
02112        * members are available, only the head caller can actually be serviced.
02113        */
02114       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02115          break;
02116       }
02117    }
02118 
02119    return avl;
02120 }

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

Definition at line 1781 of file app_queue.c.

References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), and ast_waitstream().

Referenced by say_periodic_announcement(), say_position(), and try_calling().

01782 {
01783    int res;
01784 
01785    if (ast_strlen_zero(filename)) {
01786       return 0;
01787    }
01788 
01789    ast_stopstream(chan);
01790 
01791    res = ast_streamfile(chan, filename, chan->language);
01792    if (!res)
01793       res = ast_waitstream(chan, AST_DIGIT_ANY);
01794 
01795    ast_stopstream(chan);
01796 
01797    return res;
01798 }

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

PauseQueueMember application.

Definition at line 4498 of file app_queue.c.

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

Referenced by load_module().

04499 {
04500    char *parse;
04501    AST_DECLARE_APP_ARGS(args,
04502       AST_APP_ARG(queuename);
04503       AST_APP_ARG(interface);
04504       AST_APP_ARG(options);
04505       AST_APP_ARG(reason);
04506    );
04507 
04508    if (ast_strlen_zero(data)) {
04509       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04510       return -1;
04511    }
04512 
04513    parse = ast_strdupa(data);
04514 
04515    AST_STANDARD_APP_ARGS(args, parse);
04516 
04517    if (ast_strlen_zero(args.interface)) {
04518       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04519       return -1;
04520    }
04521 
04522    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04523       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04524       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04525       return 0;
04526    }
04527 
04528    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04529 
04530    return 0;
04531 }

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

QueueLog application.

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

04689 {
04690    char *parse;
04691 
04692    AST_DECLARE_APP_ARGS(args,
04693       AST_APP_ARG(queuename);
04694       AST_APP_ARG(uniqueid);
04695       AST_APP_ARG(membername);
04696       AST_APP_ARG(event);
04697       AST_APP_ARG(params);
04698    );
04699 
04700    if (ast_strlen_zero(data)) {
04701       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04702       return -1;
04703    }
04704 
04705    parse = ast_strdupa(data);
04706 
04707    AST_STANDARD_APP_ARGS(args, parse);
04708 
04709    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04710        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04711       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04712       return -1;
04713    }
04714 
04715    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04716       "%s", args.params ? args.params : "");
04717 
04718    return 0;
04719 }

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

Definition at line 597 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

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

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 4766 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_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::cdr, queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::min_penalty, queue_ent::moh, call_queue::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_CONTINUE, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), set_queue_variables(), queue_ent::start, status, stop, penalty_rule::time, try_calling(), update_qe_rule(), update_realtime_members(), url, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04767 {
04768    int res=-1;
04769    int ringing=0;
04770    const char *user_priority;
04771    const char *max_penalty_str;
04772    const char *min_penalty_str;
04773    int prio;
04774    int qcontinue = 0;
04775    int max_penalty, min_penalty;
04776    enum queue_result reason = QUEUE_UNKNOWN;
04777    /* whether to exit Queue application after the timeout hits */
04778    int tries = 0;
04779    int noption = 0;
04780    char *parse;
04781    int makeannouncement = 0;
04782    AST_DECLARE_APP_ARGS(args,
04783       AST_APP_ARG(queuename);
04784       AST_APP_ARG(options);
04785       AST_APP_ARG(url);
04786       AST_APP_ARG(announceoverride);
04787       AST_APP_ARG(queuetimeoutstr);
04788       AST_APP_ARG(agi);
04789       AST_APP_ARG(macro);
04790       AST_APP_ARG(gosub);
04791       AST_APP_ARG(rule);
04792    );
04793    /* Our queue entry */
04794    struct queue_ent qe;
04795    
04796    if (ast_strlen_zero(data)) {
04797       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n");
04798       return -1;
04799    }
04800    
04801    parse = ast_strdupa(data);
04802    AST_STANDARD_APP_ARGS(args, parse);
04803 
04804    /* Setup our queue entry */
04805    memset(&qe, 0, sizeof(qe));
04806    qe.start = time(NULL);
04807 
04808    /* set the expire time based on the supplied timeout; */
04809    if (!ast_strlen_zero(args.queuetimeoutstr))
04810       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04811    else
04812       qe.expire = 0;
04813 
04814    /* Get the priority from the variable ${QUEUE_PRIO} */
04815    ast_channel_lock(chan);
04816    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04817    if (user_priority) {
04818       if (sscanf(user_priority, "%30d", &prio) == 1) {
04819          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04820       } else {
04821          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04822             user_priority, chan->name);
04823          prio = 0;
04824       }
04825    } else {
04826       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04827       prio = 0;
04828    }
04829 
04830    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04831 
04832    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04833       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04834          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04835       } else {
04836          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04837             max_penalty_str, chan->name);
04838          max_penalty = 0;
04839       }
04840    } else {
04841       max_penalty = 0;
04842    }
04843 
04844    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04845       if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) {
04846          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04847       } else {
04848          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04849             min_penalty_str, chan->name);
04850          min_penalty = 0;
04851       }
04852    } else {
04853       min_penalty = 0;
04854    }
04855    ast_channel_unlock(chan);
04856 
04857    if (args.options && (strchr(args.options, 'r')))
04858       ringing = 1;
04859 
04860    if (args.options && (strchr(args.options, 'c')))
04861       qcontinue = 1;
04862 
04863    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04864       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04865 
04866    qe.chan = chan;
04867    qe.prio = prio;
04868    qe.max_penalty = max_penalty;
04869    qe.min_penalty = min_penalty;
04870    qe.last_pos_said = 0;
04871    qe.last_pos = 0;
04872    qe.last_periodic_announce_time = time(NULL);
04873    qe.last_periodic_announce_sound = 0;
04874    qe.valid_digits = 0;
04875    if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04876       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04877       set_queue_result(chan, reason);
04878       return 0;
04879    }
04880    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04881       S_OR(chan->cid.cid_num, ""));
04882 check_turns:
04883    if (ringing) {
04884       ast_indicate(chan, AST_CONTROL_RINGING);
04885    } else {
04886       ast_moh_start(chan, qe.moh, NULL);
04887    }
04888 
04889    /* This is the wait loop for callers 2 through maxlen */
04890    res = wait_our_turn(&qe, ringing, &reason);
04891    if (res) {
04892       goto stop;
04893    }
04894 
04895    makeannouncement = 0;
04896 
04897    for (;;) {
04898       /* This is the wait loop for the head caller*/
04899       /* To exit, they may get their call answered; */
04900       /* they may dial a digit from the queue context; */
04901       /* or, they may timeout. */
04902 
04903       enum queue_member_status status = QUEUE_NORMAL;
04904       int exit = 0;
04905 
04906       /* Leave if we have exceeded our queuetimeout */
04907       if (qe.expire && (time(NULL) >= qe.expire)) {
04908          record_abandoned(&qe);
04909          ast_cdr_noanswer(qe.chan->cdr);
04910          reason = QUEUE_TIMEOUT;
04911          res = 0;
04912          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
04913             qe.pos, qe.opos, (long) time(NULL) - qe.start);
04914          break;
04915       }
04916 
04917       if (makeannouncement) {
04918          /* Make a position announcement, if enabled */
04919          if (qe.parent->announcefrequency)
04920             if ((res = say_position(&qe,ringing)))
04921                goto stop;
04922       }
04923       makeannouncement = 1;
04924 
04925       /* Make a periodic announcement, if enabled */
04926       if (qe.parent->periodicannouncefrequency)
04927          if ((res = say_periodic_announcement(&qe,ringing)))
04928             goto stop;
04929    
04930       /* Leave if we have exceeded our queuetimeout */
04931       if (qe.expire && (time(NULL) >= qe.expire)) {
04932          record_abandoned(&qe);
04933          ast_cdr_noanswer(qe.chan->cdr);
04934          reason = QUEUE_TIMEOUT;
04935          res = 0;
04936          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04937          break;
04938       }
04939 
04940       /* see if we need to move to the next penalty level for this queue */
04941       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
04942          update_qe_rule(&qe);
04943       }
04944 
04945       /* Try calling all queue members for 'timeout' seconds */
04946       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
04947       if (res) {
04948          goto stop;
04949       }
04950 
04951       /* exit after 'timeout' cycle if 'n' option enabled */
04952       if (noption && tries >= qe.parent->membercount) {
04953          ast_verb(3, "Exiting on time-out cycle\n");
04954          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04955          record_abandoned(&qe);
04956          ast_cdr_noanswer(qe.chan->cdr);
04957          reason = QUEUE_TIMEOUT;
04958          res = 0;
04959          break;
04960       }
04961 
04962       for (; !exit || qe.pr; update_qe_rule(&qe)) {
04963          status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
04964 
04965          if (!qe.pr || status == QUEUE_NORMAL) {
04966             break;
04967          }
04968 
04969          if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
04970                ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
04971                ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
04972             continue;
04973          } else {
04974             exit = 1;
04975          }
04976       }
04977 
04978       /* leave the queue if no agents, if enabled */
04979       if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
04980          record_abandoned(&qe);
04981          ast_cdr_noanswer(qe.chan->cdr);
04982          reason = QUEUE_LEAVEEMPTY;
04983          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04984          res = 0;
04985          break;
04986       }
04987 
04988       /* leave the queue if no reachable agents, if enabled */
04989       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
04990          record_abandoned(&qe);
04991          reason = QUEUE_LEAVEUNAVAIL;
04992          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04993          res = 0;
04994          break;
04995       }
04996       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
04997          record_abandoned(&qe);
04998          reason = QUEUE_LEAVEUNAVAIL;
04999          res = 0;
05000          break;
05001       }
05002 
05003       /* Leave if we have exceeded our queuetimeout */
05004       if (qe.expire && (time(NULL) >= qe.expire)) {
05005          record_abandoned(&qe);
05006          reason = QUEUE_TIMEOUT;
05007          res = 0;
05008          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
05009          break;
05010       }
05011 
05012       /* If using dynamic realtime members, we should regenerate the member list for this queue */
05013       update_realtime_members(qe.parent);
05014       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
05015       res = wait_a_bit(&qe);
05016       if (res)
05017          goto stop;
05018 
05019       /* Since this is a priority queue and
05020        * it is not sure that we are still at the head
05021        * of the queue, go and check for our turn again.
05022        */
05023       if (!is_our_turn(&qe)) {
05024          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
05025          goto check_turns;
05026       }
05027    }
05028 
05029 stop:
05030    if (res) {
05031       if (res < 0) {
05032          if (!qe.handled) {
05033             record_abandoned(&qe);
05034             ast_cdr_noanswer(qe.chan->cdr);
05035             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
05036                "%d|%d|%ld", qe.pos, qe.opos,
05037                (long) time(NULL) - qe.start);
05038             res = -1;
05039          } else if (qcontinue) {
05040             reason = QUEUE_CONTINUE;
05041             res = 0;
05042          }
05043       } else if (qe.valid_digits) {
05044          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
05045             "%s|%d", qe.digits, qe.pos);
05046       }
05047    }
05048 
05049    /* Don't allow return code > 0 */
05050    if (res >= 0) {
05051       res = 0; 
05052       if (ringing) {
05053          ast_indicate(chan, -1);
05054       } else {
05055          ast_moh_stop(chan);
05056       }        
05057       ast_stopstream(chan);
05058    }
05059 
05060    set_queue_variables(qe.parent, qe.chan);
05061 
05062    leave_queue(&qe);
05063    if (reason != QUEUE_UNKNOWN)
05064       set_queue_result(chan, reason);
05065 
05066    return res;
05067 }

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

05300 {
05301    int penalty;
05302    AST_DECLARE_APP_ARGS(args,
05303       AST_APP_ARG(queuename);
05304       AST_APP_ARG(interface);
05305    );
05306    /* Make sure the returned value on error is NULL. */
05307    buf[0] = '\0';
05308 
05309    if (ast_strlen_zero(data)) {
05310       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05311       return -1;
05312    }
05313 
05314    AST_STANDARD_APP_ARGS(args, data);
05315 
05316    if (args.argc < 2) {
05317       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05318       return -1;
05319    }
05320 
05321    penalty = get_member_penalty (args.queuename, args.interface);
05322    
05323    if (penalty >= 0) /* remember that buf is already '\0' */
05324       snprintf (buf, len, "%d", penalty);
05325 
05326    return 0;
05327 }

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

05331 {
05332    int penalty;
05333    AST_DECLARE_APP_ARGS(args,
05334       AST_APP_ARG(queuename);
05335       AST_APP_ARG(interface);
05336    );
05337 
05338    if (ast_strlen_zero(data)) {
05339       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05340       return -1;
05341    }
05342 
05343    AST_STANDARD_APP_ARGS(args, data);
05344 
05345    if (args.argc < 2) {
05346       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05347       return -1;
05348    }
05349 
05350    penalty = atoi(value);
05351 
05352    if (ast_strlen_zero(args.interface)) {
05353       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05354       return -1;
05355    }
05356 
05357    /* if queuename = NULL then penalty will be set for interface in all the queues. */
05358    if (set_member_penalty(args.queuename, args.interface, penalty)) {
05359       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05360       return -1;
05361    }
05362 
05363    return 0;
05364 }

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

References 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, call_queue::membercount, call_queue::members, member::paused, queue_unref(), and member::status.

05123 {
05124    int count = 0;
05125    struct member *m;
05126    struct ao2_iterator mem_iter;
05127    struct call_queue *q;
05128    char *option;
05129 
05130    if (ast_strlen_zero(data)) {
05131       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05132       return -1;
05133    }
05134 
05135    if ((option = strchr(data, ',')))
05136       *option++ = '\0';
05137    else
05138       option = "logged";
05139    if ((q = load_realtime_queue(data))) {
05140       ao2_lock(q);
05141       if (!strcasecmp(option, "logged")) {
05142          mem_iter = ao2_iterator_init(q->members, 0);
05143          while ((m = ao2_iterator_next(&mem_iter))) {
05144             /* Count the agents who are logged in and presently answering calls */
05145             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05146                count++;
05147             }
05148             ao2_ref(m, -1);
05149          }
05150       } else if (!strcasecmp(option, "free")) {
05151          mem_iter = ao2_iterator_init(q->members, 0);
05152          while ((m = ao2_iterator_next(&mem_iter))) {
05153             /* Count the agents who are logged in and presently answering calls */
05154             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05155                count++;
05156             }
05157             ao2_ref(m, -1);
05158          }
05159       } else /* must be "count" */
05160          count = q->membercount;
05161       ao2_unlock(q);
05162       queue_unref(q);
05163    } else
05164       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05165 
05166    snprintf(buf, len, "%d", count);
05167 
05168    return 0;
05169 }

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

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

05177 {
05178    int count = 0;
05179    struct member *m;
05180    struct call_queue *q;
05181    struct ao2_iterator mem_iter;
05182    static int depflag = 1;
05183 
05184    if (depflag) {
05185       depflag = 0;
05186       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");
05187    }
05188 
05189    if (ast_strlen_zero(data)) {
05190       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05191       return -1;
05192    }
05193    
05194    if ((q = load_realtime_queue(data))) {
05195       ao2_lock(q);
05196       mem_iter = ao2_iterator_init(q->members, 0);
05197       while ((m = ao2_iterator_next(&mem_iter))) {
05198          /* Count the agents who are logged in and presently answering calls */
05199          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05200             count++;
05201          }
05202          ao2_ref(m, -1);
05203       }
05204       ao2_unlock(q);
05205       queue_unref(q);
05206    } else
05207       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05208 
05209    snprintf(buf, len, "%d", count);
05210 
05211    return 0;
05212 }

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

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

05252 {
05253    struct call_queue *q, tmpq = {
05254       .name = data,  
05255    };
05256    struct member *m;
05257 
05258    /* Ensure an otherwise empty list doesn't return garbage */
05259    buf[0] = '\0';
05260 
05261    if (ast_strlen_zero(data)) {
05262       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05263       return -1;
05264    }
05265 
05266    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05267       int buflen = 0, count = 0;
05268       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05269 
05270       ao2_lock(q);
05271       while ((m = ao2_iterator_next(&mem_iter))) {
05272          /* strcat() is always faster than printf() */
05273          if (count++) {
05274             strncat(buf + buflen, ",", len - buflen - 1);
05275             buflen++;
05276          }
05277          strncat(buf + buflen, m->interface, len - buflen - 1);
05278          buflen += strlen(m->interface);
05279          /* Safeguard against overflow (negative length) */
05280          if (buflen >= len - 2) {
05281             ao2_ref(m, -1);
05282             ast_log(LOG_WARNING, "Truncating list\n");
05283             break;
05284          }
05285          ao2_ref(m, -1);
05286       }
05287       ao2_unlock(q);
05288       queue_unref(q);
05289    } else
05290       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05291 
05292    /* We should already be terminated, but let's make sure. */
05293    buf[len - 1] = '\0';
05294 
05295    return 0;
05296 }

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

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

05216 {
05217    int count = 0;
05218    struct call_queue *q, tmpq = {
05219       .name = data,  
05220    };
05221    struct ast_variable *var = NULL;
05222 
05223    buf[0] = '\0';
05224    
05225    if (ast_strlen_zero(data)) {
05226       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05227       return -1;
05228    }
05229 
05230    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05231       ao2_lock(q);
05232       count = q->count;
05233       ao2_unlock(q);
05234       queue_unref(q);
05235    } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) {
05236       /* if the queue is realtime but was not found in memory, this
05237        * means that the queue had been deleted from memory since it was 
05238        * "dead." This means it has a 0 waiting count
05239        */
05240       count = 0;
05241       ast_variables_destroy(var);
05242    } else
05243       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05244 
05245    snprintf(buf, len, "%d", count);
05246 
05247    return 0;
05248 }

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

References ao2_find, ao2_lock(), ao2_unlock(), ast_log(), ast_strlen_zero(), call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, call_queue::holdtime, int2strat(), LOG_ERROR, LOG_WARNING, call_queue::maxlen, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_unref(), queues, call_queue::servicelevel, call_queue::setqueuevar, and call_queue::strategy.

05075 {
05076    int res = -1;
05077    struct call_queue *q, tmpq = {
05078       .name = data,  
05079    };
05080 
05081    char interfacevar[256] = "";
05082    float sl = 0;
05083 
05084    if (ast_strlen_zero(data)) {
05085       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05086       return -1;
05087    }
05088 
05089    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05090       ao2_lock(q);
05091       if (q->setqueuevar) {
05092          sl = 0;
05093          res = 0;
05094 
05095          if (q->callscompleted > 0) {
05096             sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05097          }
05098 
05099          snprintf(interfacevar, sizeof(interfacevar),
05100             "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05101             q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05102 
05103          pbx_builtin_setvar_multiple(chan, interfacevar);
05104       }
05105 
05106       ao2_unlock(q);
05107       queue_unref(q);
05108    } else {
05109       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05110    }
05111 
05112    snprintf(buf, len, "%d", res);
05113 
05114    return 0;
05115 }

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

Definition at line 590 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

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

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

Definition at line 603 of file app_queue.c.

References ao2_ref.

Referenced by leave_queue(), and try_calling().

00604 {
00605    ao2_ref(q, 1);
00606    return q;
00607 }

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 1134 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, queue_ent::moh, 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 find_queue_by_name_rt(), and reload_queues().

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

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

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

05876 {
05877    switch ( cmd ) {
05878    case CLI_INIT:
05879       e->command = "queue show";
05880       e->usage =
05881          "Usage: queue show\n"
05882          "       Provides summary information on a specified queue.\n";
05883       return NULL;
05884    case CLI_GENERATE:
05885       return complete_queue_show(a->line, a->word, a->pos, a->n); 
05886    }
05887 
05888    return __queues_show(NULL, a->fd, a->argc, a->argv);
05889 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3180 of file app_queue.c.

References ast_free.

03181 {
03182    struct queue_transfer_ds *qtds = data;
03183    ast_free(qtds);
03184 }

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 3203 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, and update_queue().

03204 {
03205    struct queue_transfer_ds *qtds = data;
03206    struct queue_ent *qe = qtds->qe;
03207    struct member *member = qtds->member;
03208    time_t callstart = qtds->starttime;
03209    int callcompletedinsl = qtds->callcompletedinsl;
03210    struct ast_datastore *datastore;
03211 
03212    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d",
03213             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03214             (long) (time(NULL) - callstart), qe->opos);
03215 
03216    update_queue(qe->parent, member, callcompletedinsl);
03217    
03218    /* No need to lock the channels because they are already locked in ast_do_masquerade */
03219    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03220       ast_channel_datastore_remove(old_chan, datastore);
03221    } else {
03222       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03223    }
03224 }

static struct call_queue* queue_unref ( struct call_queue q  )  [static, read]
static void recalc_holdtime ( struct queue_ent qe,
int  newholdtime 
) [static]

Definition at line 1982 of file app_queue.c.

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

Referenced by try_calling().

01983 {
01984    int oldvalue;
01985 
01986    /* Calculate holdtime using an exponential average */
01987    /* Thanks to SRT for this contribution */
01988    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01989 
01990    ao2_lock(qe->parent);
01991    oldvalue = qe->parent->holdtime;
01992    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01993    ao2_unlock(qe->parent);
01994 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 2534 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(), and queue_ent::start.

Referenced by queue_exec(), and try_calling().

02535 {
02536    ao2_lock(qe->parent);
02537    set_queue_variables(qe->parent, qe->chan);
02538    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02539       "Queue: %s\r\n"
02540       "Uniqueid: %s\r\n"
02541       "Position: %d\r\n"
02542       "OriginalPosition: %d\r\n"
02543       "HoldTime: %d\r\n",
02544       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02545 
02546    qe->parent->callsabandoned++;
02547    ao2_unlock(qe->parent);
02548 }

static int reload ( void   )  [static]

Definition at line 6768 of file app_queue.c.

References ast_unload_realtime(), and reload_queues().

06769 {
06770    ast_unload_realtime("queue_members");
06771    reload_queues(1);
06772    return 0;
06773 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 4401 of file app_queue.c.

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

Referenced by load_module().

04402 {
04403    char *cur_ptr;
04404    const char *queue_name;
04405    char *member;
04406    char *interface;
04407    char *membername = NULL;
04408    char *state_interface;
04409    char *penalty_tok;
04410    int penalty = 0;
04411    char *paused_tok;
04412    int paused = 0;
04413    struct ast_db_entry *db_tree;
04414    struct ast_db_entry *entry;
04415    struct call_queue *cur_queue;
04416    char queue_data[PM_MAX_LEN];
04417 
04418    ao2_lock(queues);
04419 
04420    /* Each key in 'pm_family' is the name of a queue */
04421    db_tree = ast_db_gettree(pm_family, NULL);
04422    for (entry = db_tree; entry; entry = entry->next) {
04423 
04424       queue_name = entry->key + strlen(pm_family) + 2;
04425 
04426       {
04427          struct call_queue tmpq = {
04428             .name = queue_name,
04429          };
04430          cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
04431       }  
04432 
04433       if (!cur_queue)
04434          cur_queue = load_realtime_queue(queue_name);
04435 
04436       if (!cur_queue) {
04437          /* If the queue no longer exists, remove it from the
04438           * database */
04439          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04440          ast_db_del(pm_family, queue_name);
04441          continue;
04442       } 
04443 
04444       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04445          queue_unref(cur_queue);
04446          continue;
04447       }
04448 
04449       cur_ptr = queue_data;
04450       while ((member = strsep(&cur_ptr, ",|"))) {
04451          if (ast_strlen_zero(member))
04452             continue;
04453 
04454          interface = strsep(&member, ";");
04455          penalty_tok = strsep(&member, ";");
04456          paused_tok = strsep(&member, ";");
04457          membername = strsep(&member, ";");
04458          state_interface = strsep(&member, ";");
04459 
04460          if (!penalty_tok) {
04461             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04462             break;
04463          }
04464          penalty = strtol(penalty_tok, NULL, 10);
04465          if (errno == ERANGE) {
04466             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04467             break;
04468          }
04469          
04470          if (!paused_tok) {
04471             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04472             break;
04473          }
04474          paused = strtol(paused_tok, NULL, 10);
04475          if ((errno == ERANGE) || paused < 0 || paused > 1) {
04476             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04477             break;
04478          }
04479 
04480          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
04481          
04482          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04483             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04484             break;
04485          }
04486       }
04487       queue_unref(cur_queue);
04488    }
04489 
04490    ao2_unlock(queues);
04491    if (db_tree) {
04492       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04493       ast_db_freetree(db_tree);
04494    }
04495 }

static int reload_queue_rules ( int  reload  )  [static]

Definition at line 5435 of file app_queue.c.

References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_free, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, rule_list::name, ast_variable::next, rule_list::rules, and ast_variable::value.

Referenced by handle_queue_rule_reload(), and reload_queues().

05436 {
05437    struct ast_config *cfg;
05438    struct rule_list *rl_iter, *new_rl;
05439    struct penalty_rule *pr_iter;
05440    char *rulecat = NULL;
05441    struct ast_variable *rulevar = NULL;
05442    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05443    
05444    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05445       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05446    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05447       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05448       return AST_MODULE_LOAD_SUCCESS;
05449    } else {
05450       AST_LIST_LOCK(&rule_lists);
05451       while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05452          while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05453             ast_free(pr_iter);
05454          ast_free(rl_iter);
05455       }
05456       while ((rulecat = ast_category_browse(cfg, rulecat))) {
05457          if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05458             ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05459             AST_LIST_UNLOCK(&rule_lists);
05460             return AST_MODULE_LOAD_FAILURE;
05461          } else {
05462             ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05463             AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05464             for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05465                if(!strcasecmp(rulevar->name, "penaltychange")) {
05466                   insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05467                } else {
05468                   ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05469                }
05470          }
05471       }
05472       AST_LIST_UNLOCK(&rule_lists);
05473    }
05474 
05475    ast_config_destroy(cfg);
05476 
05477    return AST_MODULE_LOAD_SUCCESS;
05478 }

static int reload_queues ( int  reload  )  [static]

Definition at line 5481 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_destroy(), 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_STATUS_FILEUNCHANGED, create_queue_member(), call_queue::dead, member::delme, member::dynamic, F_AO2I_DONTLOCK, call_queue::found, init_queue(), member::interface, ast_variable::lineno, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, reload_queue_rules(), remove_from_interfaces(), member::state_interface, member::status, strat2int(), call_queue::strategy, ast_variable::value, and var.

Referenced by load_module(), and reload().

05482 {
05483    struct call_queue *q;
05484    struct ast_config *cfg;
05485    char *cat, *tmp;
05486    struct ast_variable *var;
05487    struct member *cur, *newm;
05488    struct ao2_iterator mem_iter;
05489    int new;
05490    const char *general_val = NULL;
05491    char *parse;
05492    char *interface, *state_interface;
05493    char *membername = NULL;
05494    int penalty;
05495    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05496    struct ao2_iterator queue_iter;
05497    AST_DECLARE_APP_ARGS(args,
05498       AST_APP_ARG(interface);
05499       AST_APP_ARG(penalty);
05500       AST_APP_ARG(membername);
05501       AST_APP_ARG(state_interface);
05502    );
05503 
05504    /*First things first. Let's load queuerules.conf*/
05505    if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05506       return AST_MODULE_LOAD_FAILURE;
05507       
05508    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05509       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05510       return 0;
05511    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05512       return 0;
05513    ao2_lock(queues);
05514    use_weight=0;
05515    /* Mark all queues as dead for the moment */
05516    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05517    while ((q = ao2_iterator_next(&queue_iter))) {
05518       if (!q->realtime) {
05519          q->dead = 1;
05520          q->found = 0;
05521       }
05522       queue_unref(q);
05523    }
05524 
05525    /* Chug through config file */
05526    cat = NULL;
05527    while ((cat = ast_category_browse(cfg, cat)) ) {
05528       if (!strcasecmp(cat, "general")) {  
05529          /* Initialize global settings */
05530          queue_keep_stats = 0;
05531          if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05532             queue_keep_stats = ast_true(general_val);
05533          queue_persistent_members = 0;
05534          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05535             queue_persistent_members = ast_true(general_val);
05536          autofill_default = 0;
05537          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05538             autofill_default = ast_true(general_val);
05539          montype_default = 0;
05540          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05541             if (!strcasecmp(general_val, "mixmonitor"))
05542                montype_default = 1;
05543          }
05544          update_cdr = 0;
05545          if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05546             update_cdr = ast_true(general_val);
05547          shared_lastcall = 0;
05548          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05549             shared_lastcall = ast_true(general_val);
05550       } else { /* Define queue */
05551          /* Look for an existing one */
05552          struct call_queue tmpq = {
05553             .name = cat,
05554          };
05555          if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05556             /* Make one then */
05557             if (!(q = alloc_queue(cat))) {
05558                /* TODO: Handle memory allocation failure */
05559             }
05560             new = 1;
05561          } else
05562             new = 0;
05563          if (q) {
05564             const char *tmpvar = NULL;
05565             if (!new)
05566                ao2_lock(q);
05567             /* Check if a queue with this name already exists */
05568             if (q->found) {
05569                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05570                if (!new) {
05571                   ao2_unlock(q);
05572                   queue_unref(q);
05573                }
05574                continue;
05575             }
05576             /* Due to the fact that the "linear" strategy will have a different allocation
05577              * scheme for queue members, we must devise the queue's strategy before other initializations
05578              */
05579             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05580                q->strategy = strat2int(tmpvar);
05581                if (q->strategy < 0) {
05582                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05583                   tmpvar, q->name);
05584                   q->strategy = QUEUE_STRATEGY_RINGALL;
05585                }
05586             } else
05587                q->strategy = QUEUE_STRATEGY_RINGALL;
05588             /* Re-initialize the queue, and clear statistics */
05589             init_queue(q);
05590             if (!queue_keep_stats) 
05591                clear_queue(q);
05592             mem_iter = ao2_iterator_init(q->members, 0);
05593             while ((cur = ao2_iterator_next(&mem_iter))) {
05594                if (!cur->dynamic) {
05595                   cur->delme = 1;
05596                }
05597                ao2_ref(cur, -1);
05598             }
05599             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05600                if (!strcasecmp(var->name, "member")) {
05601                   struct member tmpmem;
05602                   membername = NULL;
05603 
05604                   if (ast_strlen_zero(var->value)) {
05605                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05606                      continue;
05607                   }
05608 
05609                   /* Add a new member */
05610                   if (!(parse = ast_strdup(var->value))) {
05611                      continue;
05612                   }
05613                   
05614                   AST_STANDARD_APP_ARGS(args, parse);
05615 
05616                   interface = args.interface;
05617                   if (!ast_strlen_zero(args.penalty)) {
05618                      tmp = args.penalty;
05619                      while (*tmp && *tmp < 33) tmp++;
05620                      penalty = atoi(tmp);
05621                      if (penalty < 0) {
05622                         penalty = 0;
05623                      }
05624                   } else
05625                      penalty = 0;
05626 
05627                   if (!ast_strlen_zero(args.membername)) {
05628                      membername = args.membername;
05629                      while (*membername && *membername < 33) membername++;
05630                   }
05631 
05632                   if (!ast_strlen_zero(args.state_interface)) {
05633                      state_interface = args.state_interface;
05634                      while (*state_interface && *state_interface < 33) state_interface++;
05635                   } else
05636                      state_interface = interface;
05637 
05638                   /* Find the old position in the list */
05639                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05640                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05641                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
05642                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
05643                      remove_from_interfaces(cur->state_interface, 0);
05644                   }
05645                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05646                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05647                      add_to_interfaces(state_interface);
05648                   ao2_link(q->members, newm);
05649                   ao2_ref(newm, -1);
05650                   newm = NULL;
05651 
05652                   if (cur)
05653                      ao2_ref(cur, -1);
05654                   else {
05655                      q->membercount++;
05656                   }
05657                   ast_free(parse);
05658                } else {
05659                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05660                }
05661             }
05662 
05663             /* Free remaining members marked as delme */
05664             mem_iter = ao2_iterator_init(q->members, 0);
05665             while ((cur = ao2_iterator_next(&mem_iter))) {
05666                if (! cur->delme) {
05667                   ao2_ref(cur, -1);
05668                   continue;
05669                }
05670                q->membercount--;
05671                ao2_unlink(q->members, cur);
05672                remove_from_interfaces(cur->interface, 0);
05673                ao2_ref(cur, -1);
05674             }
05675 
05676             if (new) {
05677                ao2_link(queues, q);
05678             } else 
05679                ao2_unlock(q);
05680             queue_unref(q);
05681          }
05682       }
05683    }
05684    ast_config_destroy(cfg);
05685    queue_iter = ao2_iterator_init(queues, 0);
05686    while ((q = ao2_iterator_next(&queue_iter))) {
05687       if (q->dead) {
05688          ao2_unlink(queues, q);
05689       } else {
05690          ao2_lock(q);
05691          mem_iter = ao2_iterator_init(q->members, 0);
05692          while ((cur = ao2_iterator_next(&mem_iter))) {
05693             if (cur->dynamic)
05694                q->membercount++;
05695             cur->status = ast_device_state(cur->state_interface);
05696             ao2_ref(cur, -1);
05697          }
05698          ao2_unlock(q);
05699       }
05700       queue_unref(q);
05701    }
05702    ao2_unlock(queues);
05703    return 1;
05704 }

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

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

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

01015 {
01016    struct member_interface *curint;
01017 
01018    if (interface_exists_global(interface, lock_queue_container))
01019       return 0;
01020 
01021    AST_LIST_LOCK(&interfaces);
01022    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01023       if (!strcasecmp(curint->interface, interface)) {
01024          ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01025          AST_LIST_REMOVE_CURRENT(list);
01026          ast_free(curint);
01027          break;
01028       }
01029    }
01030    AST_LIST_TRAVERSE_SAFE_END;
01031    AST_LIST_UNLOCK(&interfaces);
01032 
01033    return 0;
01034 }

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

References ao2_find, ao2_lock(), ao2_ref, 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_unref(), queues, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::state_interface.

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

04139 {
04140    struct call_queue *q, tmpq = {
04141       .name = queuename,   
04142    };
04143    struct member *mem, tmpmem;
04144    int res = RES_NOSUCHQUEUE;
04145 
04146    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04147    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04148       ao2_lock(queues);
04149       ao2_lock(q);
04150       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04151          /* XXX future changes should beware of this assumption!! */
04152          if (!mem->dynamic) {
04153             ao2_ref(mem, -1);
04154             ao2_unlock(q);
04155             queue_unref(q);
04156             ao2_unlock(queues);
04157             return RES_NOT_DYNAMIC;
04158          }
04159          q->membercount--;
04160          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04161             "Queue: %s\r\n"
04162             "Location: %s\r\n"
04163             "MemberName: %s\r\n",
04164             q->name, mem->interface, mem->membername);
04165          ao2_unlink(q->members, mem);
04166          remove_from_interfaces(mem->state_interface, 0);
04167          ao2_ref(mem, -1);
04168 
04169          if (queue_persistent_members)
04170             dump_queue_members(q);
04171          
04172          res = RES_OKAY;
04173       } else {
04174          res = RES_EXISTS;
04175       }
04176       ao2_unlock(q);
04177       ao2_unlock(queues);
04178       queue_unref(q);
04179    }
04180 
04181    return res;
04182 }

static int ring_entry ( struct queue_ent qe,
struct callattempt tmp,
int *  busies 
) [static]

Part 2 of ring_one.

Does error checking before attempting to request a channel and call a member. This function is only called from ring_one(). Failure can occur if:

  • Agent on call
  • Agent is paused
  • Wrapup time not expired
  • Priority by another queue
Return values:
1 on success to reach a free agent
0 on failure to get agent.

Definition at line 2219 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_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, 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, 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, update_status(), ast_cdr::userfield, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

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

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

02397 {
02398    int ret = 0;
02399 
02400    while (ret == 0) {
02401       struct callattempt *best = find_best(outgoing);
02402       if (!best) {
02403          ast_debug(1, "Nobody left to try ringing in queue\n");
02404          break;
02405       }
02406       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02407          struct callattempt *cur;
02408          /* Ring everyone who shares this best metric (for ringall) */
02409          for (cur = outgoing; cur; cur = cur->q_next) {
02410             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02411                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02412                ret |= ring_entry(qe, cur, busies);
02413             }
02414          }
02415       } else {
02416          /* Ring just the best channel */
02417          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02418          ret = ring_entry(qe, best, busies);
02419       }
02420       
02421       /* If we have timed out, break out */
02422       if (qe->expire && (time(NULL) >= qe->expire)) {
02423          ast_debug(1, "Queue timed out while ringing members.\n");
02424          ret = 0;
02425          break;
02426       }
02427    }
02428 
02429    return ret;
02430 }

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 2551 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, call_queue::name, queue_ent::parent, and set_member_paused().

Referenced by wait_for_answer().

02552 {
02553    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02554    if (qe->parent->eventwhencalled)
02555       manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer",
02556                   "Queue: %s\r\n"
02557                   "Uniqueid: %s\r\n"
02558                   "Channel: %s\r\n"
02559                   "Member: %s\r\n"
02560                   "MemberName: %s\r\n"
02561                   "Ringtime: %d\r\n",
02562                   qe->parent->name,
02563                   qe->chan->uniqueid,
02564                   qe->chan->name,
02565                   interface,
02566                   membername,
02567                   rnatime);
02568    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02569    if (qe->parent->autopause && pause) {
02570       if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02571          ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02572       } else {
02573          ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02574       }
02575    }
02576    return;
02577 }

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

RemoveQueueMember application.

Definition at line 4570 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(), LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

04571 {
04572    int res=-1;
04573    char *parse, *temppos = NULL;
04574    AST_DECLARE_APP_ARGS(args,
04575       AST_APP_ARG(queuename);
04576       AST_APP_ARG(interface);
04577       AST_APP_ARG(options);
04578    );
04579 
04580 
04581    if (ast_strlen_zero(data)) {
04582       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04583       return -1;
04584    }
04585 
04586    parse = ast_strdupa(data);
04587 
04588    AST_STANDARD_APP_ARGS(args, parse);
04589 
04590    if (ast_strlen_zero(args.interface)) {
04591       args.interface = ast_strdupa(chan->name);
04592       temppos = strrchr(args.interface, '-');
04593       if (temppos)
04594          *temppos = '\0';
04595    }
04596 
04597    switch (remove_from_queue(args.queuename, args.interface)) {
04598    case RES_OKAY:
04599       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04600       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04601       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04602       res = 0;
04603       break;
04604    case RES_EXISTS:
04605       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04606       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04607       res = 0;
04608       break;
04609    case RES_NOSUCHQUEUE:
04610       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04611       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04612       res = 0;
04613       break;
04614    case RES_NOT_DYNAMIC:
04615       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04616       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04617       res = 0;
04618       break;
04619    }
04620 
04621    return res;
04622 }

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 memeber exists create one flag it as a RT member and add to queue member list.

Definition at line 1347 of file app_queue.c.

References add_to_interfaces(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_queue_log(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, remove_from_interfaces(), member::rt_uniqueid, and member::state_interface.

Referenced by find_queue_by_name_rt(), and update_realtime_members().

01348 {
01349    struct member *m;
01350    struct ao2_iterator mem_iter;
01351    int penalty = 0;
01352    int paused  = 0;
01353    int found = 0;
01354 
01355    if (penalty_str) {
01356       penalty = atoi(penalty_str);
01357       if (penalty < 0)
01358          penalty = 0;
01359    }
01360 
01361    if (paused_str) {
01362       paused = atoi(paused_str);
01363       if (paused < 0)
01364          paused = 0;
01365    }
01366 
01367    /* Find member by realtime uniqueid and update */
01368    mem_iter = ao2_iterator_init(q->members, 0);
01369    while ((m = ao2_iterator_next(&mem_iter))) {
01370       if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) {
01371          m->dead = 0;   /* Do not delete this one. */
01372          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01373          if (paused_str)
01374             m->paused = paused;
01375          if (strcasecmp(state_interface, m->state_interface)) {
01376             remove_from_interfaces(m->state_interface, 0);
01377             ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01378             add_to_interfaces(m->state_interface);
01379          }     
01380          m->penalty = penalty;
01381          found = 1;
01382          ao2_ref(m, -1);
01383          break;
01384       }
01385       ao2_ref(m, -1);
01386    }
01387 
01388    /* Create a new member */
01389    if (!found) {
01390       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01391          m->dead = 0;
01392          m->realtime = 1;
01393          ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid));
01394          add_to_interfaces(m->state_interface);
01395          ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", "");
01396          ao2_link(q->members, m);
01397          ao2_ref(m, -1);
01398          m = NULL;
01399          q->membercount++;
01400       }
01401    }
01402 }

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

Playback announcement to queued members if peroid has elapsed.

Definition at line 2481 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, ast_str::str, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

02482 {
02483    int res = 0;
02484    time_t now;
02485 
02486    /* Get the current time */
02487    time(&now);
02488 
02489    /* Check to see if it is time to announce */
02490    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02491       return 0;
02492 
02493    /* Stop the music on hold so we can play our own file */
02494    if (ringing)
02495       ast_indicate(qe->chan,-1);
02496    else
02497       ast_moh_stop(qe->chan);
02498 
02499    ast_verb(3, "Playing periodic announcement\n");
02500    
02501    if (qe->parent->randomperiodicannounce) {
02502       qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce;
02503    } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 
02504       ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02505       qe->last_periodic_announce_sound = 0;
02506    }
02507    
02508    /* play the announcement */
02509    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02510 
02511    if (res > 0 && !valid_exit(qe, res))
02512       res = 0;
02513 
02514    /* Resume Music on Hold if the caller is going to stay in the queue */
02515    if (!res) {
02516       if (ringing)
02517          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02518       else
02519          ast_moh_start(qe->chan, qe->moh, NULL);
02520    }
02521 
02522    /* update last_periodic_announce_time */
02523    qe->last_periodic_announce_time = now;
02524 
02525    /* Update the current periodic announcement to the next announcement */
02526    if (!qe->parent->randomperiodicannounce) {
02527       qe->last_periodic_announce_sound++;
02528    }
02529    
02530    return res;
02531 }

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

Definition at line 1838 of file app_queue.c.

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

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

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

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

Referenced by try_calling().

03140 {
03141    const char *reason = NULL; /* silence dumb compilers */
03142 
03143    if (!qe->parent->eventwhencalled)
03144       return;
03145 
03146    switch (rsn) {
03147    case CALLER:
03148       reason = "caller";
03149       break;
03150    case AGENT:
03151       reason = "agent";
03152       break;
03153    case TRANSFER:
03154       reason = "transfer";
03155       break;
03156    }
03157 
03158    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03159       "Queue: %s\r\n"
03160       "Uniqueid: %s\r\n"
03161       "Channel: %s\r\n"
03162       "Member: %s\r\n"
03163       "MemberName: %s\r\n"
03164       "HoldTime: %ld\r\n"
03165       "TalkTime: %ld\r\n"
03166       "Reason: %s\r\n"
03167       "%s",
03168       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03169       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03170       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03171 }

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

Definition at line 4246 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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_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().

04247 {
04248    int found = 0;
04249    struct call_queue *q;
04250    struct member *mem;
04251    struct ao2_iterator queue_iter;
04252    int failed;
04253 
04254    /* Special event for when all queues are paused - individual events still generated */
04255    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
04256    if (ast_strlen_zero(queuename))
04257       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04258 
04259    queue_iter = ao2_iterator_init(queues, 0);
04260    while ((q = ao2_iterator_next(&queue_iter))) {
04261       ao2_lock(q);
04262       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04263          if ((mem = interface_exists(q, interface))) {
04264             if (mem->paused == paused) {
04265                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04266             }
04267 
04268             failed = 0;
04269             if (mem->realtime) {
04270                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04271             }
04272          
04273             if (failed) {
04274                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04275                ao2_ref(mem, -1);
04276                ao2_unlock(q);
04277                continue;
04278             }  
04279             found++;
04280             mem->paused = paused;
04281 
04282             if (queue_persistent_members)
04283                dump_queue_members(q);
04284 
04285             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04286             
04287             if (!ast_strlen_zero(reason)) {
04288                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04289                   "Queue: %s\r\n"
04290                   "Location: %s\r\n"
04291                   "MemberName: %s\r\n"
04292                   "Paused: %d\r\n"
04293                   "Reason: %s\r\n",
04294                      q->name, mem->interface, mem->membername, paused, reason);
04295             } else {
04296                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04297                   "Queue: %s\r\n"
04298                   "Location: %s\r\n"
04299                   "MemberName: %s\r\n"
04300                   "Paused: %d\r\n",
04301                      q->name, mem->interface, mem->membername, paused);
04302             }
04303             ao2_ref(mem, -1);
04304          }
04305       }
04306       
04307       if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) {
04308          ao2_unlock(q);
04309          queue_unref(q);
04310          break;
04311       }
04312       
04313       ao2_unlock(q);
04314       queue_unref(q);
04315    }
04316 
04317    return found ? RESULT_SUCCESS : RESULT_FAILURE;
04318 }

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

Definition at line 4321 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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_unref(), queues, RESULT_FAILURE, and RESULT_SUCCESS.

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

04322 {
04323    int foundinterface = 0, foundqueue = 0;
04324    struct call_queue *q;
04325    struct member *mem;
04326    struct ao2_iterator queue_iter;
04327 
04328    if (penalty < 0) {
04329       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04330       return RESULT_FAILURE;
04331    }
04332 
04333    queue_iter = ao2_iterator_init(queues, 0);
04334    while ((q = ao2_iterator_next(&queue_iter))) {
04335       ao2_lock(q);
04336       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04337          foundqueue++;
04338          if ((mem = interface_exists(q, interface))) {
04339             foundinterface++;
04340             mem->penalty = penalty;
04341             
04342             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04343             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04344                "Queue: %s\r\n"
04345                "Location: %s\r\n"
04346                "Penalty: %d\r\n",
04347                q->name, mem->interface, penalty);
04348             ao2_ref(mem, -1);
04349          }
04350       }
04351       ao2_unlock(q);
04352       queue_unref(q);
04353    }
04354 
04355    if (foundinterface) {
04356       return RESULT_SUCCESS;
04357    } else if (!foundqueue) {
04358       ast_log (LOG_ERROR, "Invalid queuename\n"); 
04359    } else {
04360       ast_log (LOG_ERROR, "Invalid interface\n");
04361    }  
04362 
04363    return RESULT_FAILURE;
04364 }

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

sets the QUEUESTATUS channel variable

Definition at line 554 of file app_queue.c.

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

Referenced by queue_exec().

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

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

Set variables of queue.

Definition at line 616 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, call_queue::holdtime, int2strat(), call_queue::maxlen, call_queue::name, pbx_builtin_setvar_multiple(), call_queue::servicelevel, call_queue::setqueuevar, and call_queue::strategy.

Referenced by end_bridge_callback(), queue_exec(), record_abandoned(), and try_calling().

00617 {
00618    char interfacevar[256]="";
00619    float sl = 0;
00620 
00621    if (q->setqueuevar) {
00622       sl = 0;
00623       if (q->callscompleted > 0) 
00624          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00625 
00626       snprintf(interfacevar, sizeof(interfacevar),
00627          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00628          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
00629    
00630       pbx_builtin_setvar_multiple(chan, interfacevar); 
00631    }
00632 }

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

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

Definition at line 3241 of file app_queue.c.

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

Referenced by try_calling().

03242 {
03243    struct ast_datastore *ds;
03244    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03245 
03246    if (!qtds) {
03247       ast_log(LOG_WARNING, "Memory allocation error!\n");
03248       return NULL;
03249    }
03250 
03251    ast_channel_lock(qe->chan);
03252    if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) {
03253       ast_channel_unlock(qe->chan);
03254       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03255       return NULL;
03256    }
03257 
03258    qtds->qe = qe;
03259    /* This member is refcounted in try_calling, so no need to add it here, too */
03260    qtds->member = member;
03261    qtds->starttime = starttime;
03262    qtds->callcompletedinsl = callcompletedinsl;
03263    ds->data = qtds;
03264    ast_channel_datastore_add(qe->chan, ds);
03265    ast_channel_unlock(qe->chan);
03266    return ds;
03267 }

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

02458 {
02459    struct callattempt *best = find_best(outgoing);
02460 
02461    if (best) {
02462       /* Ring just the best channel */
02463       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02464       qe->linpos = best->metric % 1000;
02465    } else {
02466       /* Just increment rrpos */
02467       if (qe->linwrapped) {
02468          /* No more channels, start over */
02469          qe->linpos = 0;
02470       } else {
02471          /* Prioritize next entry */
02472          qe->linpos++;
02473       }
02474    }
02475    qe->linwrapped = 0;
02476 
02477    return 0;
02478 }

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

02434 {
02435    struct callattempt *best = find_best(outgoing);
02436 
02437    if (best) {
02438       /* Ring just the best channel */
02439       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02440       qe->parent->rrpos = best->metric % 1000;
02441    } else {
02442       /* Just increment rrpos */
02443       if (qe->parent->wrapped) {
02444          /* No more channels, start over */
02445          qe->parent->rrpos = 0;
02446       } else {
02447          /* Prioritize next entry */
02448          qe->parent->rrpos++;
02449       }
02450    }
02451    qe->parent->wrapped = 0;
02452 
02453    return 0;
02454 }

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

Definition at line 578 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

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

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

References ast_channel::_state, AGENT, queue_ent::announce, ao2_alloc, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), asprintf, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_failed(), AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_cdr_noanswer(), ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_datastore_alloc, ast_datastore_free(), ast_debug, AST_DIGIT_ANY, AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_free, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_pbx_run_args(), ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), CALLER, member::calls, ast_channel::cdr, queue_end_bridge::chan, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, member::dynamic, end_bridge_callback(), ast_bridge_config::end_bridge_callback, ast_bridge_config::end_bridge_callback_data, end_bridge_callback_data_fixup(), ast_bridge_config::end_bridge_callback_data_fixup, errno, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event, callattempt::member, call_queue::membercount, call_queue::memberdelay, call_queue::membergosub, call_queue::membermacro, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, call_queue::name, ast_pbx_args::no_hangup_chan, callattempt::oldstatus, queue_ent::opos, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_multiple(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), member::penalty, queue_ent::pending, play_file(), queue_ent::pos, ast_channel::priority, queue_end_bridge::q, callattempt::q_next, QUEUE_EVENT_VARIABLES, queue_ref(), QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, queue_transfer_info, queues, member::realtime, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), send_agent_complete(), call_queue::servicelevel, set_queue_variables(), call_queue::setinterfacevar, call_queue::setqueueentryvar, setup_transfer_datastore(), call_queue::sound_callerannounce, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, queue_ent::start, member::status, callattempt::stillgoing, store_next_lin(), store_next_rr(), call_queue::strategy, ast_channel::tech, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, TRANSFER, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.

Referenced by queue_exec().

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

static int unload_module ( void   )  [static]

Definition at line 6662 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, 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, queue_unref(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, and queuewaitingcount_function.

06663 {
06664    int res;
06665    struct ast_context *con;
06666    struct ao2_iterator q_iter;
06667    struct call_queue *q = NULL;
06668 
06669    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06670    res = ast_manager_unregister("QueueStatus");
06671    res |= ast_manager_unregister("Queues");
06672    res |= ast_manager_unregister("QueueRule");
06673    res |= ast_manager_unregister("QueueSummary");
06674    res |= ast_manager_unregister("QueueAdd");
06675    res |= ast_manager_unregister("QueueRemove");
06676    res |= ast_manager_unregister("QueuePause");
06677    res |= ast_manager_unregister("QueueLog");
06678    res |= ast_manager_unregister("QueuePenalty");
06679    res |= ast_unregister_application(app_aqm);
06680    res |= ast_unregister_application(app_rqm);
06681    res |= ast_unregister_application(app_pqm);
06682    res |= ast_unregister_application(app_upqm);
06683    res |= ast_unregister_application(app_ql);
06684    res |= ast_unregister_application(app);
06685    res |= ast_custom_function_unregister(&queuevar_function);
06686    res |= ast_custom_function_unregister(&queuemembercount_function);
06687    res |= ast_custom_function_unregister(&queuemembercount_dep);
06688    res |= ast_custom_function_unregister(&queuememberlist_function);
06689    res |= ast_custom_function_unregister(&queuewaitingcount_function);
06690    res |= ast_custom_function_unregister(&queuememberpenalty_function);
06691 
06692    if (device_state_sub)
06693       ast_event_unsubscribe(device_state_sub);
06694 
06695    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06696       ast_context_remove_extension2(con, "s", 1, NULL, 0);
06697       ast_context_destroy(con, "app_queue"); /* leave no trace */
06698    }
06699 
06700    clear_and_free_interfaces();
06701 
06702    q_iter = ao2_iterator_init(queues, 0);
06703    while ((q = ao2_iterator_next(&q_iter))) {
06704       ao2_unlink(queues, q);
06705       queue_unref(q);
06706    }
06707    ao2_ref(queues, -1);
06708    devicestate_tps = ast_taskprocessor_unreference(devicestate_tps);
06709    ast_unload_realtime("queue_members");
06710    return res;
06711 }

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

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

02891 {
02892    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02893    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02894    char max_penalty_str[20], min_penalty_str[20]; 
02895    /* a relative change to the penalty could put it below 0 */
02896    if (max_penalty < 0)
02897       max_penalty = 0;
02898    if (min_penalty < 0)
02899       min_penalty = 0;
02900    if (min_penalty > max_penalty)
02901       min_penalty = max_penalty;
02902    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02903    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02904    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02905    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02906    qe->max_penalty = max_penalty;
02907    qe->min_penalty = min_penalty;
02908    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);
02909    qe->pr = AST_LIST_NEXT(qe->pr, list);
02910 }

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

References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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(), and try_calling().

03032 {
03033    struct member *mem;
03034    struct call_queue *qtmp;
03035    struct ao2_iterator queue_iter;  
03036    
03037    if (shared_lastcall) {
03038       queue_iter = ao2_iterator_init(queues, 0);
03039       while ((qtmp = ao2_iterator_next(&queue_iter))) {
03040          ao2_lock(qtmp);
03041          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
03042             time(&mem->lastcall);
03043             mem->calls++;
03044             mem->lastqueue = q;
03045             ao2_ref(mem, -1);
03046          }
03047          ao2_unlock(qtmp);
03048          ao2_ref(qtmp, -1);
03049       }
03050    } else {
03051       ao2_lock(q);
03052       time(&member->lastcall);
03053       member->calls++;
03054       member->lastqueue = q;
03055       ao2_unlock(q);
03056    }  
03057    ao2_lock(q);
03058    q->callscompleted++;
03059    if (callcompletedinsl)
03060       q->callscompletedinsl++;
03061    ao2_unlock(q);
03062    return 0;
03063 }

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

Definition at line 1638 of file app_queue.c.

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

Referenced by set_member_paused().

01639 {
01640    int ret = -1;
01641 
01642    if (ast_strlen_zero(mem->rt_uniqueid))
01643       return ret;
01644 
01645    if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0)
01646       ret = 0;
01647 
01648    return ret;
01649 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1652 of file app_queue.c.

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

01653 {
01654    struct ast_config *member_config = NULL;
01655    struct member *m;
01656    char *interface = NULL;
01657    struct ao2_iterator mem_iter;
01658 
01659    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) {
01660       /*This queue doesn't have realtime members*/
01661       ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name);
01662       return;
01663    }
01664 
01665    ao2_lock(queues);
01666    ao2_lock(q);
01667    
01668    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01669    mem_iter = ao2_iterator_init(q->members, 0);
01670    while ((m = ao2_iterator_next(&mem_iter))) {
01671       if (m->realtime)
01672          m->dead = 1;
01673       ao2_ref(m, -1);
01674    }
01675 
01676    while ((interface = ast_category_browse(member_config, interface))) {
01677       rt_handle_member_record(q, interface,
01678          ast_variable_retrieve(member_config, interface, "uniqueid"),
01679          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01680          ast_variable_retrieve(member_config, interface, "penalty"),
01681          ast_variable_retrieve(member_config, interface, "paused"),
01682          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01683    }
01684 
01685    /* Delete all realtime members that have been deleted in DB. */
01686    mem_iter = ao2_iterator_init(q->members, 0);
01687    while ((m = ao2_iterator_next(&mem_iter))) {
01688       if (m->dead) {
01689          ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", "");
01690          ao2_unlink(q->members, m);
01691          remove_from_interfaces(m->state_interface, 0);
01692          q->membercount--;
01693       }
01694       ao2_ref(m, -1);
01695    }
01696    ao2_unlock(q);
01697    ao2_unlock(queues);
01698    ast_config_destroy(member_config);
01699 }

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

References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, 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_unref(), queues, member::realtime, member::state_interface, and member::status.

Referenced by handle_statechange(), and ring_entry().

00715 {
00716    struct member *cur;
00717    struct ao2_iterator mem_iter, queue_iter;
00718    struct call_queue *q;
00719    char tmp_interface[80];
00720 
00721    queue_iter = ao2_iterator_init(queues, 0);
00722    while ((q = ao2_iterator_next(&queue_iter))) {
00723       ao2_lock(q);
00724       mem_iter = ao2_iterator_init(q->members, 0);
00725       while ((cur = ao2_iterator_next(&mem_iter))) {
00726          char *slash_pos;
00727          ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
00728          if ((slash_pos = strchr(tmp_interface, '/')))
00729             if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/')))
00730                *slash_pos = '\0';
00731 
00732          if (strcasecmp(interface, tmp_interface)) {
00733             ao2_ref(cur, -1);
00734             continue;
00735          }
00736 
00737          if (cur->status != status) {
00738             cur->status = status;
00739             if (q->maskmemberstatus) {
00740                ao2_ref(cur, -1);
00741                continue;
00742             }
00743 
00744             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00745                "Queue: %s\r\n"
00746                "Location: %s\r\n"
00747                "MemberName: %s\r\n"
00748                "Membership: %s\r\n"
00749                "Penalty: %d\r\n"
00750                "CallsTaken: %d\r\n"
00751                "LastCall: %d\r\n"
00752                "Status: %d\r\n"
00753                "Paused: %d\r\n",
00754                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00755                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00756          }
00757          ao2_ref(cur, -1);
00758       }
00759       queue_unref(q);
00760       ao2_unlock(q);
00761    }
00762 
00763    return 0;
00764 }

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

UnPauseQueueMember application.

Definition at line 4534 of file app_queue.c.

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

Referenced by load_module().

04535 {
04536    char *parse;
04537    AST_DECLARE_APP_ARGS(args,
04538       AST_APP_ARG(queuename);
04539       AST_APP_ARG(interface);
04540       AST_APP_ARG(options);
04541       AST_APP_ARG(reason);
04542    );
04543 
04544    if (ast_strlen_zero(data)) {
04545       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04546       return -1;
04547    }
04548 
04549    parse = ast_strdupa(data);
04550 
04551    AST_STANDARD_APP_ARGS(args, parse);
04552 
04553    if (ast_strlen_zero(args.interface)) {
04554       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04555       return -1;
04556    }
04557 
04558    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04559       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04560       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04561       return 0;
04562    }
04563 
04564    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04565 
04566    return 0;
04567 }

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 1805 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(), wait_for_answer(), and wait_our_turn().

01806 {
01807    int digitlen = strlen(qe->digits);
01808 
01809    /* Prevent possible buffer overflow */
01810    if (digitlen < sizeof(qe->digits) - 2) {
01811       qe->digits[digitlen] = digit;
01812       qe->digits[digitlen + 1] = '\0';
01813    } else {
01814       qe->digits[0] = '\0';
01815       return 0;
01816    }
01817 
01818    /* If there's no context to goto, short-circuit */
01819    if (ast_strlen_zero(qe->context))
01820       return 0;
01821 
01822    /* If the extension is bad, then reset the digits to blank */
01823    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01824       qe->digits[0] = '\0';
01825       return 0;
01826    }
01827 
01828    /* We have an exact match */
01829    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01830       qe->valid_digits = 1;
01831       /* Return 1 on a successful goto */
01832       return 1;
01833    }
01834 
01835    return 0;
01836 }

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

References ast_copy_string(), ast_str_alloca, buf, pbx_builtin_serialize_variables(), and ast_str::str.

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

02169 {
02170    struct ast_str *buf = ast_str_alloca(len + 1);
02171    char *tmp;
02172 
02173    if (pbx_builtin_serialize_variables(chan, &buf)) {
02174       int i, j;
02175 
02176       /* convert "\n" to "\nVariable: " */
02177       strcpy(vars, "Variable: ");
02178       tmp = buf->str;
02179 
02180       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02181          vars[j] = tmp[i];
02182 
02183          if (tmp[i + 1] == '\0')
02184             break;
02185          if (tmp[i] == '\n') {
02186             vars[j++] = '\r';
02187             vars[j++] = '\n';
02188 
02189             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02190             j += 9;
02191          }
02192       }
02193       if (j > len - 3)
02194          j = len - 3;
02195       vars[j++] = '\r';
02196       vars[j++] = '\n';
02197       vars[j] = '\0';
02198    } else {
02199       /* there are no channel variables; leave it blank */
02200       *vars = '\0';
02201    }
02202    return vars;
02203 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 4057 of file app_queue.c.

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

Referenced by queue_exec().

04058 {
04059    /* Don't need to hold the lock while we setup the outgoing calls */
04060    int retrywait = qe->parent->retry * 1000;
04061 
04062    int res = ast_waitfordigit(qe->chan, retrywait);
04063    if (res > 0 && !valid_exit(qe, res))
04064       res = 0;
04065 
04066    return res;
04067 }

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, read]

Wait for a member to answer the call.

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

Definition at line 2590 of file app_queue.c.

References ast_channel::_state, accountcode, ast_call(), ast_cdr_busy(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_free, ast_frfree, ast_hangup(), ast_log(), AST_MAX_WATCHERS, ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), ast_request(), AST_STATE_UP, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_waitfor_n(), callattempt::call_next, ast_channel::cdr, ast_channel::cdrflags, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, ast_frame::data, do_hang(), ast_channel::exten, f, ast_frame::frametype, ast_channel::hangupcause, callattempt::interface, member::interface, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, queue_ent::parent, queue_ent::pos, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), rna(), S_OR, queue_ent::start, starttime, status, callattempt::stillgoing, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, ast_frame::uint32, and valid_exit().

Referenced by try_calling().

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

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 2922 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_NORMAL, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, penalty_rule::time, update_qe_rule(), and valid_exit().

Referenced by queue_exec().

02923 {
02924    int res = 0;
02925 
02926    /* This is the holding pen for callers 2 through maxlen */
02927    for (;;) {
02928       enum queue_member_status status = QUEUE_NORMAL;
02929       int exit = 0;
02930 
02931       if (is_our_turn(qe))
02932          break;
02933 
02934       /* If we have timed out, break out */
02935       if (qe->expire && (time(NULL) >= qe->expire)) {
02936          *reason = QUEUE_TIMEOUT;
02937          break;
02938       }
02939 
02940       /* If we are going to exit due to a leavewhenempty condition, we should
02941        * actually attempt to keep the caller in the queue until we have
02942        * exhausted all penalty rules.
02943        */
02944       for (; !exit || qe->pr; update_qe_rule(qe)) {
02945          status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
02946   
02947          if (!qe->pr || status == QUEUE_NORMAL) {
02948             break;
02949          }
02950 
02951          /* leave the queue if no agents, if enabled */
02952          if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) ||
02953                ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
02954                ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) {
02955             continue;
02956          } else {
02957             exit = 1;
02958          }
02959       }
02960 
02961       if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) {
02962          *reason = QUEUE_LEAVEEMPTY;
02963          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02964          leave_queue(qe);
02965          break;
02966       }
02967 
02968       /* leave the queue if no reachable agents, if enabled */
02969       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
02970          *reason = QUEUE_LEAVEUNAVAIL;
02971          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02972          leave_queue(qe);
02973          break;
02974       }
02975       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) {
02976          *reason = QUEUE_LEAVEUNAVAIL;
02977          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02978          leave_queue(qe);
02979          break;
02980       }
02981 
02982       /* Make a position announcement, if enabled */
02983       if (qe->parent->announcefrequency &&
02984          (res = say_position(qe,ringing)))
02985          break;
02986 
02987       /* If we have timed out, break out */
02988       if (qe->expire && (time(NULL) >= qe->expire)) {
02989          *reason = QUEUE_TIMEOUT;
02990          break;
02991       }
02992 
02993       /* Make a periodic announcement, if enabled */
02994       if (qe->parent->periodicannouncefrequency &&
02995          (res = say_periodic_announcement(qe,ringing)))
02996          break;
02997       
02998       /* see if we need to move to the next penalty level for this queue */
02999       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
03000          update_qe_rule(qe);
03001       }
03002 
03003       /* If we have timed out, break out */
03004       if (qe->expire && (time(NULL) >= qe->expire)) {
03005          *reason = QUEUE_TIMEOUT;
03006          break;
03007       }
03008       
03009       /* Wait a second before checking again */
03010       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
03011          if (res > 0 && !valid_exit(qe, res))
03012             res = 0;
03013          else
03014             break;
03015       }
03016       
03017       /* If we have timed out, break out */
03018       if (qe->expire && (time(NULL) >= qe->expire)) {
03019          *reason = QUEUE_TIMEOUT;
03020          break;
03021       }
03022    }
03023 
03024    return res;
03025 }


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

char* app = "Queue" [static]

Definition at line 152 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 211 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 213 of file app_queue.c.

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

Definition at line 212 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 237 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 239 of file app_queue.c.

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

Definition at line 238 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 271 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 224 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 226 of file app_queue.c.

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

Definition at line 225 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 255 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 257 of file app_queue.c.

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

Definition at line 256 of file app_queue.c.

Definition at line 6779 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 292 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 6652 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* descrip [static]

Definition at line 156 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 301 of file app_queue.c.

Definition at line 136 of file app_queue.c.

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

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

Persistent Members astdb family.

Definition at line 278 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 6643 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 6649 of file app_queue.c.

int queue_keep_stats = 0 [static]

queues.conf [general] option

Definition at line 283 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 286 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

Initial value:
 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}

a datastore used to help correctly log attended transfers of queue callers

Definition at line 3188 of file app_queue.c.

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

Definition at line 5397 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 5384 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 5416 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 5425 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 5366 of file app_queue.c.

Referenced by load_module(), and unload_module().

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

int shared_lastcall = 0 [static]

queues.conf [general] option

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

char* text
int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 304 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 289 of file app_queue.c.


Generated on 3 Mar 2010 for Asterisk - the Open Source PBX by  doxygen 1.6.1