Thu Jul 9 13:40:47 2009

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"

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 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
}

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 * device_state_thread (void *data)
 Consumer of the statechange queue.
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 void * handle_statechange (struct statechange *sc)
 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 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 *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 statechange_queue (const char *dev, enum ast_device_state state)
 Producer of the statechange queue.
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 = "068e67f60f50dd9ee86464c05884a49d" , .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 const 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
struct {
   ast_cond_t   cond
   ast_mutex_t   lock
   struct {
      statechange *   first
      statechange *   last
   }   state_change_q
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
static struct ast_event_subdevice_state_sub
 Subscription to device state change events.
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static int queue_keep_stats = 0
 queues.conf [general] option
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queuemembercount_dep
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpenalty_function
static struct ao2_containerqueues
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 0
 queues.conf [general] option
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int update_cdr = 0
 queues.conf [general] option
static int use_weight = 0
 queues.conf per-queue weight option


Detailed Description

True call queues with optional send URL on answer.

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

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

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

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 410 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 411 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

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

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 136 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 137 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

The maximum periodic announcements we can have

Definition at line 139 of file app_queue.c.

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 142 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 278 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_LOOSE   3

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

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

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

#define RECHECK   1

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

Definition at line 138 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Entry already exists

Definition at line 145 of file app_queue.c.

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

#define RES_NOSUCHQUEUE   (-3)

No such queue

Definition at line 147 of file app_queue.c.

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

#define RES_NOT_DYNAMIC   (-4)

Member is not dynamic

Definition at line 148 of file app_queue.c.

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

#define RES_OKAY   0

Action completed

Definition at line 144 of file app_queue.c.

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

#define RES_OUTOFMEMORY   (-2)

Out of memory

Definition at line 146 of file app_queue.c.

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


Enumeration Type Documentation

anonymous enum

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

Definition at line 112 of file app_queue.c.

enum agent_complete_reason

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3086 of file app_queue.c.

03086                            {
03087    CALLER,
03088    AGENT,
03089    TRANSFER
03090 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 634 of file app_queue.c.

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 304 of file app_queue.c.

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


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 5648 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, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_unref(), queues, member::realtime, call_queue::realtime, s, 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().

05649 {
05650    struct call_queue *q;
05651    struct ast_str *out = ast_str_alloca(240);
05652    int found = 0;
05653    time_t now = time(NULL);
05654    struct ao2_iterator queue_iter;
05655    struct ao2_iterator mem_iter;
05656 
05657    if (argc != 2 && argc != 3)
05658       return CLI_SHOWUSAGE;
05659 
05660    if (argc == 3) { /* specific queue */
05661       if ((q = load_realtime_queue(argv[2]))) {
05662          queue_unref(q);
05663       }
05664    } else if (ast_check_realtime("queues")) {
05665       /* This block is to find any queues which are defined in realtime but
05666        * which have not yet been added to the in-core container
05667        */
05668       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
05669       char *queuename;
05670       if (cfg) {
05671          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05672             if ((q = load_realtime_queue(queuename))) {
05673                queue_unref(q);
05674             }
05675          }
05676          ast_config_destroy(cfg);
05677       }
05678    }
05679 
05680    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05681    ao2_lock(queues);
05682    while ((q = ao2_iterator_next(&queue_iter))) {
05683       float sl;
05684       struct call_queue *realtime_queue = NULL;
05685 
05686       ao2_lock(q);
05687       /* This check is to make sure we don't print information for realtime
05688        * queues which have been deleted from realtime but which have not yet
05689        * been deleted from the in-core container
05690        */
05691       if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) {
05692          ao2_unlock(q);
05693          queue_unref(q);
05694          continue;
05695       } else if (q->realtime) {
05696          queue_unref(realtime_queue);
05697       }
05698       if (argc == 3 && strcasecmp(q->name, argv[2])) {
05699          ao2_unlock(q);
05700          queue_unref(q);
05701          continue;
05702       }
05703       found = 1;
05704 
05705       ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count);
05706       if (q->maxlen)
05707          ast_str_append(&out, 0, "%d", q->maxlen);
05708       else
05709          ast_str_append(&out, 0, "unlimited");
05710       sl = 0;
05711       if (q->callscompleted > 0)
05712          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05713       ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds",
05714          int2strat(q->strategy), q->holdtime, q->weight,
05715          q->callscompleted, q->callsabandoned,sl,q->servicelevel);
05716       do_print(s, fd, out->str);
05717       if (!ao2_container_count(q->members))
05718          do_print(s, fd, "   No Members");
05719       else {
05720          struct member *mem;
05721 
05722          do_print(s, fd, "   Members: ");
05723          mem_iter = ao2_iterator_init(q->members, 0);
05724          while ((mem = ao2_iterator_next(&mem_iter))) {
05725             ast_str_set(&out, 0, "      %s", mem->membername);
05726             if (strcasecmp(mem->membername, mem->interface)) {
05727                ast_str_append(&out, 0, " (%s)", mem->interface);
05728             }
05729             if (mem->penalty)
05730                ast_str_append(&out, 0, " with penalty %d", mem->penalty);
05731             ast_str_append(&out, 0, "%s%s%s (%s)",
05732                mem->dynamic ? " (dynamic)" : "",
05733                mem->realtime ? " (realtime)" : "",
05734                mem->paused ? " (paused)" : "",
05735                devstate2str(mem->status));
05736             if (mem->calls)
05737                ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)",
05738                   mem->calls, (long) (time(NULL) - mem->lastcall));
05739             else
05740                ast_str_append(&out, 0, " has taken no calls yet");
05741             do_print(s, fd, out->str);
05742             ao2_ref(mem, -1);
05743          }
05744       }
05745       if (!q->head)
05746          do_print(s, fd, "   No Callers");
05747       else {
05748          struct queue_ent *qe;
05749          int pos = 1;
05750 
05751          do_print(s, fd, "   Callers: ");
05752          for (qe = q->head; qe; qe = qe->next) {
05753             ast_str_set(&out, 0, "      %d. %s (wait: %ld:%2.2ld, prio: %d)",
05754                pos++, qe->chan->name, (long) (now - qe->start) / 60,
05755                (long) (now - qe->start) % 60, qe->prio);
05756             do_print(s, fd, out->str);
05757          }
05758       }
05759       do_print(s, fd, ""); /* blank line between entries */
05760       ao2_unlock(q);
05761       queue_unref(q); /* Unref the iterator's reference */
05762    }
05763    ao2_unlock(queues);
05764    if (!found) {
05765       if (argc == 3)
05766          ast_str_set(&out, 0, "No such queue: %s.", argv[2]);
05767       else
05768          ast_str_set(&out, 0, "No queues.");
05769       do_print(s, fd, out->str);
05770    }
05771    return CLI_SUCCESS;
05772 }

static void __reg_module ( void   )  [static]

Definition at line 6707 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 6707 of file app_queue.c.

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

Definition at line 994 of file app_queue.c.

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

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

00995 {
00996    struct member_interface *curint;
00997 
00998    AST_LIST_LOCK(&interfaces);
00999    AST_LIST_TRAVERSE(&interfaces, curint, list) {
01000       if (!strcasecmp(curint->interface, interface))
01001          break;
01002    }
01003 
01004    if (curint) {
01005       AST_LIST_UNLOCK(&interfaces);
01006       return 0;
01007    }
01008 
01009    ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
01010    
01011    if ((curint = ast_calloc(1, sizeof(*curint)))) {
01012       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
01013       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
01014    }
01015    AST_LIST_UNLOCK(&interfaces);
01016 
01017    return 0;
01018 }

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

04132 {
04133    struct call_queue *q;
04134    struct member *new_member, *old_member;
04135    int res = RES_NOSUCHQUEUE;
04136 
04137    /*! \note Ensure the appropriate realtime queue is loaded.  Note that this
04138     * short-circuits if the queue is already in memory. */
04139    if (!(q = load_realtime_queue(queuename)))
04140       return res;
04141 
04142    ao2_lock(queues);
04143 
04144    ao2_lock(q);
04145    if ((old_member = interface_exists(q, interface)) == NULL) {
04146       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
04147          add_to_interfaces(new_member->state_interface);
04148          new_member->dynamic = 1;
04149          ao2_link(q->members, new_member);
04150          q->membercount++;
04151          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
04152             "Queue: %s\r\n"
04153             "Location: %s\r\n"
04154             "MemberName: %s\r\n"
04155             "Membership: %s\r\n"
04156             "Penalty: %d\r\n"
04157             "CallsTaken: %d\r\n"
04158             "LastCall: %d\r\n"
04159             "Status: %d\r\n"
04160             "Paused: %d\r\n",
04161             q->name, new_member->interface, new_member->membername,
04162             "dynamic",
04163             new_member->penalty, new_member->calls, (int) new_member->lastcall,
04164             new_member->status, new_member->paused);
04165          
04166          ao2_ref(new_member, -1);
04167          new_member = NULL;
04168 
04169          if (dump)
04170             dump_queue_members(q);
04171          
04172          res = RES_OKAY;
04173       } else {
04174          res = RES_OUTOFMEMORY;
04175       }
04176    } else {
04177       ao2_ref(old_member, -1);
04178       res = RES_EXISTS;
04179    }
04180    ao2_unlock(q);
04181    ao2_unlock(queues);
04182 
04183    return res;
04184 }

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

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

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

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

AddQueueMember application.

Definition at line 4559 of file app_queue.c.

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

Referenced by load_module().

04560 {
04561    int res=-1;
04562    char *parse, *temppos = NULL;
04563    AST_DECLARE_APP_ARGS(args,
04564       AST_APP_ARG(queuename);
04565       AST_APP_ARG(interface);
04566       AST_APP_ARG(penalty);
04567       AST_APP_ARG(options);
04568       AST_APP_ARG(membername);
04569       AST_APP_ARG(state_interface);
04570    );
04571    int penalty = 0;
04572 
04573    if (ast_strlen_zero(data)) {
04574       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n");
04575       return -1;
04576    }
04577 
04578    parse = ast_strdupa(data);
04579 
04580    AST_STANDARD_APP_ARGS(args, parse);
04581 
04582    if (ast_strlen_zero(args.interface)) {
04583       args.interface = ast_strdupa(chan->name);
04584       temppos = strrchr(args.interface, '-');
04585       if (temppos)
04586          *temppos = '\0';
04587    }
04588 
04589    if (!ast_strlen_zero(args.penalty)) {
04590       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
04591          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04592          penalty = 0;
04593       }
04594    }
04595 
04596    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04597    case RES_OKAY:
04598       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04599       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04600       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04601       res = 0;
04602       break;
04603    case RES_EXISTS:
04604       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04605       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04606       res = 0;
04607       break;
04608    case RES_NOSUCHQUEUE:
04609       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04610       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04611       res = 0;
04612       break;
04613    case RES_OUTOFMEMORY:
04614       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04615       break;
04616    }
04617 
04618    return res;
04619 }

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

References ast_channel_datastore_find(), chan, and queue_transfer_info.

03191 {
03192    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
03193 }

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

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

Referenced by try_calling().

03030 {
03031    if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty)))
03032       return -1;
03033 
03034    switch (q->strategy) {
03035    case QUEUE_STRATEGY_RINGALL:
03036       /* Everyone equal, except for penalty */
03037       tmp->metric = mem->penalty * 1000000;
03038       break;
03039    case QUEUE_STRATEGY_LINEAR:
03040       if (pos < qe->linpos) {
03041          tmp->metric = 1000 + pos;
03042       } else {
03043          if (pos > qe->linpos)
03044             /* Indicate there is another priority */
03045             qe->linwrapped = 1;
03046          tmp->metric = pos;
03047       }
03048       tmp->metric += mem->penalty * 1000000;
03049       break;
03050    case QUEUE_STRATEGY_RRMEMORY:
03051       if (pos < q->rrpos) {
03052          tmp->metric = 1000 + pos;
03053       } else {
03054          if (pos > q->rrpos)
03055             /* Indicate there is another priority */
03056             q->wrapped = 1;
03057          tmp->metric = pos;
03058       }
03059       tmp->metric += mem->penalty * 1000000;
03060       break;
03061    case QUEUE_STRATEGY_RANDOM:
03062       tmp->metric = ast_random() % 1000;
03063       tmp->metric += mem->penalty * 1000000;
03064       break;
03065    case QUEUE_STRATEGY_WRANDOM:
03066       tmp->metric = ast_random() % ((1 + mem->penalty) * 1000);
03067       break;
03068    case QUEUE_STRATEGY_FEWESTCALLS:
03069       tmp->metric = mem->calls;
03070       tmp->metric += mem->penalty * 1000000;
03071       break;
03072    case QUEUE_STRATEGY_LEASTRECENT:
03073       if (!mem->lastcall)
03074          tmp->metric = 0;
03075       else
03076          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
03077       tmp->metric += mem->penalty * 1000000;
03078       break;
03079    default:
03080       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
03081       break;
03082    }
03083    return 0;
03084 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1068 of file app_queue.c.

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

Referenced by unload_module().

01069 {
01070    struct member_interface *curint;
01071 
01072    AST_LIST_LOCK(&interfaces);
01073    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01074       ast_free(curint);
01075    AST_LIST_UNLOCK(&interfaces);
01076 }

static void clear_queue ( struct call_queue q  )  [static]

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

00986 {
00987    q->holdtime = 0;
00988    q->callscompleted = 0;
00989    q->callsabandoned = 0;
00990    q->callscompletedinsl = 0;
00991    q->wrapuptime = 0;
00992 }

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

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

02104 {
02105    struct call_queue *q;
02106    struct member *mem;
02107    int found = 0;
02108    struct ao2_iterator queue_iter;
02109    
02110    /* q's lock and rq's lock already set by try_calling()
02111     * to solve deadlock */
02112    queue_iter = ao2_iterator_init(queues, 0);
02113    while ((q = ao2_iterator_next(&queue_iter))) {
02114       if (q == rq) { /* don't check myself, could deadlock */
02115          queue_unref(q);
02116          continue;
02117       }
02118       ao2_lock(q);
02119       if (q->count && q->members) {
02120          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
02121             ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
02122             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
02123                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);
02124                found = 1;
02125             }
02126             ao2_ref(mem, -1);
02127          }
02128       }
02129       ao2_unlock(q);
02130       queue_unref(q);
02131       if (found) {
02132          break;
02133       }
02134    }
02135    return found;
02136 }

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

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

05775 {
05776    struct call_queue *q;
05777    char *ret = NULL;
05778    int which = 0;
05779    int wordlen = strlen(word);
05780    struct ao2_iterator queue_iter;
05781 
05782    queue_iter = ao2_iterator_init(queues, 0);
05783    while ((q = ao2_iterator_next(&queue_iter))) {
05784       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05785          ret = ast_strdup(q->name);
05786          queue_unref(q);
05787          break;
05788       }
05789       queue_unref(q);
05790    }
05791 
05792    return ret;
05793 }

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

Definition at line 6151 of file app_queue.c.

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

Referenced by handle_queue_add_member().

06152 {
06153    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
06154    switch (pos) {
06155    case 3: /* Don't attempt to complete name of interface (infinite possibilities) */
06156       return NULL;
06157    case 4: /* only one possible match, "to" */
06158       return state == 0 ? ast_strdup("to") : NULL;
06159    case 5: /* <queue> */
06160       return complete_queue(line, word, pos, state);
06161    case 6: /* only one possible match, "penalty" */
06162       return state == 0 ? ast_strdup("penalty") : NULL;
06163    case 7:
06164       if (state < 100) {      /* 0-99 */
06165          char *num;
06166          if ((num = ast_malloc(3))) {
06167             sprintf(num, "%d", state);
06168          }
06169          return num;
06170       } else {
06171          return NULL;
06172       }
06173    case 8: /* only one possible match, "as" */
06174       return state == 0 ? ast_strdup("as") : NULL;
06175    case 9: /* Don't attempt to complete name of member (infinite possibilities) */
06176       return NULL;
06177    default:
06178       return NULL;
06179    }
06180 }

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

Definition at line 6362 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

06363 {
06364    /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */
06365    switch (pos) {
06366    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
06367       return NULL;
06368    case 4:  /* only one possible match, "queue" */
06369       return state == 0 ? ast_strdup("queue") : NULL;
06370    case 5:  /* <queue> */
06371       return complete_queue(line, word, pos, state);
06372    case 6: /* "reason" */
06373       return state == 0 ? ast_strdup("reason") : NULL;
06374    case 7: /* Can't autocomplete a reason, since it's 100% customizeable */
06375       return NULL;
06376    default:
06377       return NULL;
06378    }
06379 }

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

Definition at line 6280 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, queue_unref(), and queues.

Referenced by handle_queue_remove_member().

06281 {
06282    int which = 0;
06283    struct call_queue *q;
06284    struct member *m;
06285    struct ao2_iterator queue_iter;
06286    struct ao2_iterator mem_iter;
06287    int wordlen = strlen(word);
06288 
06289    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
06290    if (pos > 5 || pos < 3)
06291       return NULL;
06292    if (pos == 4)   /* only one possible match, 'from' */
06293       return (state == 0 ? ast_strdup("from") : NULL);
06294 
06295    if (pos == 5)   /* No need to duplicate code */
06296       return complete_queue(line, word, pos, state);
06297 
06298    /* here is the case for 3, <member> */
06299    queue_iter = ao2_iterator_init(queues, 0);
06300    while ((q = ao2_iterator_next(&queue_iter))) {
06301       ao2_lock(q);
06302       mem_iter = ao2_iterator_init(q->members, 0);
06303       while ((m = ao2_iterator_next(&mem_iter))) {
06304          if (!strncasecmp(word, m->membername, wordlen) && ++which > state) {
06305             char *tmp;
06306             ao2_unlock(q);
06307             tmp = ast_strdup(m->interface);
06308             ao2_ref(m, -1);
06309             queue_unref(q);
06310             return tmp;
06311          }
06312          ao2_ref(m, -1);
06313       }
06314       ao2_unlock(q);
06315       queue_unref(q);
06316    }
06317 
06318    return NULL;
06319 }

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

Definition at line 6495 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdup, rule_list::list, LOG_DEBUG, and rule_list::name.

Referenced by handle_queue_rule_show().

06496 {
06497    int which = 0;
06498    struct rule_list *rl_iter;
06499    int wordlen = strlen(word);
06500    char *ret = NULL;
06501    if (pos != 3) /* Wha? */ {
06502       ast_log(LOG_DEBUG, "Hitting this???, pos is %d\n", pos);
06503       return NULL;
06504    }
06505 
06506    AST_LIST_LOCK(&rule_lists);
06507    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06508       if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) {
06509          ret = ast_strdup(rl_iter->name);
06510          break;
06511       }
06512    }
06513    AST_LIST_UNLOCK(&rule_lists);
06514 
06515    return ret;
06516 }

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

Definition at line 6432 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

06433 {
06434    /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/
06435    switch (pos) {
06436    case 4:
06437       if (state == 0) {
06438          return ast_strdup("on");
06439       } else {
06440          return NULL;
06441       }
06442    case 6:
06443       if (state == 0) {
06444          return ast_strdup("in");
06445       } else {
06446          return NULL;
06447       }
06448    case 7:
06449       return complete_queue(line, word, pos, state);
06450    default:
06451       return NULL;
06452    }
06453 }

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

Definition at line 5795 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

05796 {
05797    if (pos == 2)
05798       return complete_queue(line, word, pos, state);
05799    return NULL;
05800 }

static int compress_char ( const char  c  )  [static]

Definition at line 889 of file app_queue.c.

00890 {
00891    if (c < 32)
00892       return 0;
00893    else if (c > 96)
00894       return c - 64;
00895    else
00896       return c - 32;
00897 }

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

Copy rule from global list into specified queue.

Definition at line 4656 of file app_queue.c.

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

Referenced by join_queue().

04657 {
04658    struct penalty_rule *pr_iter;
04659    struct rule_list *rl_iter;
04660    const char *tmp = rulename;
04661    if (ast_strlen_zero(tmp)) {
04662       return;
04663    }
04664    AST_LIST_LOCK(&rule_lists);
04665    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
04666       if (!strcasecmp(rl_iter->name, tmp))
04667          break;
04668    }
04669    if (rl_iter) {
04670       AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
04671          struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr));
04672          if (!new_pr) {
04673             ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n");
04674             AST_LIST_UNLOCK(&rule_lists);
04675             break;
04676          }
04677          new_pr->time = pr_iter->time;
04678          new_pr->max_value = pr_iter->max_value;
04679          new_pr->min_value = pr_iter->min_value;
04680          new_pr->max_relative = pr_iter->max_relative;
04681          new_pr->min_relative = pr_iter->min_relative;
04682          AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list);
04683       }
04684    }
04685    AST_LIST_UNLOCK(&rule_lists);
04686 }

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

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

Definition at line 864 of file app_queue.c.

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

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

00865 {
00866    struct member *cur;
00867    
00868    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00869       cur->penalty = penalty;
00870       cur->paused = paused;
00871       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00872       if (!ast_strlen_zero(state_interface))
00873          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00874       else
00875          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00876       if (!ast_strlen_zero(membername))
00877          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00878       else
00879          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00880       if (!strchr(cur->interface, '/'))
00881          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00882       cur->status = ast_device_state(cur->state_interface);
00883    }
00884 
00885    return cur;
00886 }

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

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

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

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

Definition at line 847 of file app_queue.c.

References ast_device_state(), ast_event_get_ie_str(), ast_event_get_ie_uint(), AST_EVENT_IE_DEVICE, AST_EVENT_IE_STATE, ast_log(), ast_strlen_zero(), LOG_ERROR, and statechange_queue().

00848 {
00849    enum ast_device_state state;
00850    const char *device;
00851 
00852    state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE);
00853    device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE);
00854 
00855    if (ast_strlen_zero(device)) {
00856       ast_log(LOG_ERROR, "Received invalid event that had no device IE\n");
00857       return;
00858    }
00859 
00860    statechange_queue(device, state);
00861 }

static void* device_state_thread ( void *  data  )  [static]

Consumer of the statechange queue.

Definition at line 794 of file app_queue.c.

References ast_cond_wait(), ast_free, AST_LIST_REMOVE_HEAD, ast_mutex_lock(), ast_mutex_unlock(), device_state, statechange::entry, and handle_statechange().

00795 {
00796    struct statechange *sc = NULL;
00797 
00798    while (!device_state.stop) {
00799       ast_mutex_lock(&device_state.lock);
00800       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00801          ast_cond_wait(&device_state.cond, &device_state.lock);
00802          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00803       }
00804       ast_mutex_unlock(&device_state.lock);
00805 
00806       /* Check to see if we were woken up to see the request to stop */
00807       if (device_state.stop)
00808          break;
00809 
00810       if (!sc)
00811          continue;
00812 
00813       handle_statechange(sc);
00814 
00815       ast_free(sc);
00816       sc = NULL;
00817    }
00818 
00819    if (sc)
00820       ast_free(sc);
00821 
00822    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00823       ast_free(sc);
00824 
00825    return NULL;
00826 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2139 of file app_queue.c.

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

Referenced by ring_entry().

02140 {
02141    o->stillgoing = 0;
02142    ast_hangup(o->chan);
02143    o->chan = NULL;
02144 }

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

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

Referenced by __queues_show().

05635 {
05636    if (s)
05637       astman_append(s, "%s\r\n", str);
05638    else
05639       ast_cli(fd, "%s\n", str);
05640 }

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

04033 {
04034    struct member *cur_member;
04035    char value[PM_MAX_LEN];
04036    int value_len = 0;
04037    int res;
04038    struct ao2_iterator mem_iter;
04039 
04040    memset(value, 0, sizeof(value));
04041 
04042    if (!pm_queue)
04043       return;
04044 
04045    mem_iter = ao2_iterator_init(pm_queue->members, 0);
04046    while ((cur_member = ao2_iterator_next(&mem_iter))) {
04047       if (!cur_member->dynamic) {
04048          ao2_ref(cur_member, -1);
04049          continue;
04050       }
04051 
04052       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
04053          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
04054 
04055       ao2_ref(cur_member, -1);
04056 
04057       if (res != strlen(value + value_len)) {
04058          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
04059          break;
04060       }
04061       value_len += res;
04062    }
04063    
04064    if (value_len && !cur_member) {
04065       if (ast_db_put(pm_family, pm_queue->name, value))
04066          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
04067    } else
04068       /* Delete the entry if the queue is empty or there is an error */
04069       ast_db_del(pm_family, pm_queue->name);
04070 }

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 3237 of file app_queue.c.

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

03238 {
03239    struct queue_end_bridge *qeb = data;
03240    struct call_queue *q = qeb->q;
03241    struct ast_channel *chan = qeb->chan;
03242 
03243    if (ao2_ref(qeb, -1) == 1) {
03244       ao2_lock(q);
03245       set_queue_variables(q, chan);
03246       ao2_unlock(q);
03247       /* This unrefs the reference we made in try_calling when we allocated qeb */
03248       queue_unref(q);
03249    }
03250 }

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

Definition at line 3230 of file app_queue.c.

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

03231 {
03232    struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data;
03233    ao2_ref(qeb, +1);
03234    qeb->chan = originator;
03235 }

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

find the entry with the best metric, or NULL

Definition at line 2353 of file app_queue.c.

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

02354 {
02355    struct callattempt *best = NULL, *cur;
02356 
02357    for (cur = outgoing; cur; cur = cur->q_next) {
02358       if (cur->stillgoing &&              /* Not already done */
02359          !cur->chan &&              /* Isn't already going */
02360          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02361          best = cur;
02362       }
02363    }
02364 
02365    return best;
02366 }

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

Reload a single queue via realtime.

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

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

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

Definition at line 1459 of file app_queue.c.

References alloc_queue(), ao2_find(), ao2_lock(), ao2_unlink(), ao2_unlock(), ast_debug, ast_log(), clear_queue(), LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

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

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

Iterate through queue's member list and delete them.

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

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

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

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

04304 {
04305    int foundqueue = 0, penalty;
04306    struct call_queue *q, tmpq = {
04307       .name = queuename,   
04308    };
04309    struct member *mem;
04310    
04311    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04312       foundqueue = 1;
04313       ao2_lock(q);
04314       if ((mem = interface_exists(q, interface))) {
04315          penalty = mem->penalty;
04316          ao2_ref(mem, -1);
04317          ao2_unlock(q);
04318          queue_unref(q);
04319          return penalty;
04320       }
04321       ao2_unlock(q);
04322       queue_unref(q);
04323    }
04324 
04325    /* some useful debuging */
04326    if (foundqueue) 
04327       ast_log (LOG_ERROR, "Invalid queuename\n");
04328    else 
04329       ast_log (LOG_ERROR, "Invalid interface\n");
04330 
04331    return RESULT_FAILURE;
04332 }

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

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

00648 {
00649    struct member *member;
00650    struct ao2_iterator mem_iter;
00651    enum queue_member_status result = QUEUE_NO_MEMBERS;
00652 
00653    ao2_lock(q);
00654    mem_iter = ao2_iterator_init(q->members, 0);
00655    for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) {
00656       if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty)))
00657          continue;
00658 
00659       switch (member->status) {
00660       case AST_DEVICE_INVALID:
00661          /* nothing to do */
00662          break;
00663       case AST_DEVICE_UNAVAILABLE:
00664          if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS) 
00665             result = QUEUE_NO_REACHABLE_MEMBERS;
00666          break;
00667       default:
00668          if (member->paused) {
00669             result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS;
00670          } else {
00671             ao2_unlock(q);
00672             ao2_ref(member, -1);
00673             return QUEUE_NORMAL;
00674          }
00675          break;
00676       }
00677    }
00678 
00679    ao2_unlock(q);
00680    return result;
00681 }

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

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

06208 {
06209    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
06210    int penalty;
06211 
06212    switch ( cmd ) {
06213    case CLI_INIT:
06214       e->command = "queue add member";
06215       e->usage =
06216          "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n"; 
06217       return NULL;
06218    case CLI_GENERATE:
06219       return complete_queue_add_member(a->line, a->word, a->pos, a->n);
06220    }
06221 
06222    if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) {
06223       return CLI_SHOWUSAGE;
06224    } else if (strcmp(a->argv[4], "to")) {
06225       return CLI_SHOWUSAGE;
06226    } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) {
06227       return CLI_SHOWUSAGE;
06228    } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) {
06229       return CLI_SHOWUSAGE;
06230    } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) {
06231       return CLI_SHOWUSAGE;
06232    }
06233 
06234    queuename = a->argv[5];
06235    interface = a->argv[3];
06236    if (a->argc >= 8) {
06237       if (sscanf(a->argv[7], "%d", &penalty) == 1) {
06238          if (penalty < 0) {
06239             ast_cli(a->fd, "Penalty must be >= 0\n");
06240             penalty = 0;
06241          }
06242       } else {
06243          ast_cli(a->fd, "Penalty must be an integer >= 0\n");
06244          penalty = 0;
06245       }
06246    } else {
06247       penalty = 0;
06248    }
06249 
06250    if (a->argc >= 10) {
06251       membername = a->argv[9];
06252    }
06253 
06254    if (a->argc >= 12) {
06255       state_interface = a->argv[11];
06256    }
06257 
06258    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
06259    case RES_OKAY:
06260       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
06261       ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
06262       return CLI_SUCCESS;
06263    case RES_EXISTS:
06264       ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
06265       return CLI_FAILURE;
06266    case RES_NOSUCHQUEUE:
06267       ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
06268       return CLI_FAILURE;
06269    case RES_OUTOFMEMORY:
06270       ast_cli(a->fd, "Out of memory\n");
06271       return CLI_FAILURE;
06272    case RES_NOT_DYNAMIC:
06273       ast_cli(a->fd, "Member not dynamic\n");
06274       return CLI_FAILURE;
06275    default:
06276       return CLI_FAILURE;
06277    }
06278 }

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

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

06382 {
06383    char *queuename, *interface, *reason;
06384    int paused;
06385 
06386    switch (cmd) {
06387    case CLI_INIT:
06388       e->command = "queue {pause|unpause} member";
06389       e->usage = 
06390          "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n"
06391          "     Pause or unpause a queue member. Not specifying a particular queue\n"
06392          "     will pause or unpause a member across all queues to which the member\n"
06393          "     belongs.\n";
06394       return NULL;
06395    case CLI_GENERATE:
06396       return complete_queue_pause_member(a->line, a-> word, a->pos, a->n);
06397    }
06398 
06399    if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) {
06400       return CLI_SHOWUSAGE;
06401    } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) {
06402       return CLI_SHOWUSAGE;
06403    } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) {
06404       return CLI_SHOWUSAGE;
06405    }
06406 
06407 
06408    interface = a->argv[3];
06409    queuename = a->argc >= 6 ? a->argv[5] : NULL;
06410    reason = a->argc == 8 ? a->argv[7] : NULL;
06411    paused = !strcasecmp(a->argv[1], "pause");
06412 
06413    if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) {
06414       ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface);
06415       if (!ast_strlen_zero(queuename))
06416          ast_cli(a->fd, " in queue '%s'", queuename);
06417       if (!ast_strlen_zero(reason))
06418          ast_cli(a->fd, " for reason '%s'", reason);
06419       ast_cli(a->fd, "\n");
06420       return CLI_SUCCESS;
06421    } else {
06422       ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface);
06423       if (!ast_strlen_zero(queuename))
06424          ast_cli(a->fd, " in queue '%s'", queuename);
06425       if (!ast_strlen_zero(reason))
06426          ast_cli(a->fd, " for reason '%s'", reason);
06427       ast_cli(a->fd, "\n");
06428       return CLI_FAILURE;
06429    }
06430 }

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

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

06322 {
06323    char *queuename, *interface;
06324 
06325    switch (cmd) {
06326    case CLI_INIT:
06327       e->command = "queue remove member";
06328       e->usage = "Usage: queue remove member <channel> from <queue>\n"; 
06329       return NULL;
06330    case CLI_GENERATE:
06331       return complete_queue_remove_member(a->line, a->word, a->pos, a->n);
06332    }
06333 
06334    if (a->argc != 6) {
06335       return CLI_SHOWUSAGE;
06336    } else if (strcmp(a->argv[4], "from")) {
06337       return CLI_SHOWUSAGE;
06338    }
06339 
06340    queuename = a->argv[5];
06341    interface = a->argv[3];
06342 
06343    switch (remove_from_queue(queuename, interface)) {
06344    case RES_OKAY:
06345       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
06346       ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
06347       return CLI_SUCCESS;
06348    case RES_EXISTS:
06349       ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
06350       return CLI_FAILURE;
06351    case RES_NOSUCHQUEUE:
06352       ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
06353       return CLI_FAILURE;
06354    case RES_OUTOFMEMORY:
06355       ast_cli(a->fd, "Out of memory\n");
06356       return CLI_FAILURE;
06357    default:
06358       return CLI_FAILURE;
06359    }
06360 }

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

Definition at line 6552 of file app_queue.c.

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

06553 {
06554    switch (cmd) {
06555       case CLI_INIT:
06556          e->command = "queue rules reload";
06557          e->usage = 
06558             "Usage: queue rules reload\n"
06559             "Reloads rules defined in queuerules.conf\n";
06560          return NULL;
06561       case CLI_GENERATE:
06562          return NULL;
06563    }
06564    reload_queue_rules(1);
06565    return CLI_SUCCESS;
06566 }

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

Definition at line 6518 of file app_queue.c.

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

06519 {
06520    char *rule;
06521    struct rule_list *rl_iter;
06522    struct penalty_rule *pr_iter;
06523    switch (cmd) {
06524    case CLI_INIT:
06525       e->command = "queue rules show";
06526       e->usage =
06527       "Usage: queue rules show [rulename]\n"
06528       "Show the list of rules associated with rulename. If no\n"
06529       "rulename is specified, list all rules defined in queuerules.conf\n";
06530       return NULL;
06531    case CLI_GENERATE:
06532       return complete_queue_rule_show(a->line, a->word, a->pos, a->n);
06533    }
06534 
06535    if (a->argc != 3 && a->argc != 4)
06536       return CLI_SHOWUSAGE;
06537 
06538    rule = a->argc == 4 ? a->argv[3] : "";
06539    AST_LIST_LOCK(&rule_lists);
06540    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
06541       if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) {
06542          ast_cli(a->fd, "Rule: %s\n", rl_iter->name);
06543          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
06544             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);
06545          }
06546       }
06547    }
06548    AST_LIST_UNLOCK(&rule_lists);
06549    return CLI_SUCCESS; 
06550 }

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

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

06456 {
06457    char *queuename = NULL, *interface;
06458    int penalty = 0;
06459 
06460    switch (cmd) {
06461    case CLI_INIT:
06462       e->command = "queue set penalty";
06463       e->usage = 
06464       "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n"
06465       "Set a member's penalty in the queue specified. If no queue is specified\n"
06466       "then that interface's penalty is set in all queues to which that interface is a member\n";
06467       return NULL;
06468    case CLI_GENERATE:
06469       return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n);
06470    }
06471 
06472    if (a->argc != 6 && a->argc != 8) {
06473       return CLI_SHOWUSAGE;
06474    } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) {
06475       return CLI_SHOWUSAGE;
06476    }
06477 
06478    if (a->argc == 8)
06479       queuename = a->argv[7];
06480    interface = a->argv[5];
06481    penalty = atoi(a->argv[3]);
06482 
06483    switch (set_member_penalty(queuename, interface, penalty)) {
06484    case RESULT_SUCCESS:
06485       ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06486       return CLI_SUCCESS;
06487    case RESULT_FAILURE:
06488       ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename);
06489       return CLI_FAILURE;
06490    default:
06491       return CLI_FAILURE;
06492    }
06493 }

static void* handle_statechange ( struct statechange sc  )  [static]

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

Definition at line 747 of file app_queue.c.

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

00748 {
00749    struct member_interface *curint;
00750    char interface[80];
00751 
00752    AST_LIST_LOCK(&interfaces);
00753    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00754       char *slash_pos;
00755       ast_copy_string(interface, curint->interface, sizeof(interface));
00756       if ((slash_pos = strchr(interface, '/')))
00757          if ((slash_pos = strchr(slash_pos + 1, '/')))
00758             *slash_pos = '\0';
00759 
00760       if (!strcasecmp(interface, sc->dev))
00761          break;
00762    }
00763    AST_LIST_UNLOCK(&interfaces);
00764 
00765    if (!curint) {
00766       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));
00767       return NULL;
00768    }
00769 
00770    ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00771 
00772    update_status(sc->dev, sc->state);
00773 
00774    return NULL;
00775 }

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

Hang up a list of outgoing calls.

Definition at line 2037 of file app_queue.c.

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

02038 {
02039    struct callattempt *oo;
02040 
02041    while (outgoing) {
02042       /* Hangup any existing lines we have open */
02043       if (outgoing->chan && (outgoing->chan != exception))
02044          ast_hangup(outgoing->chan);
02045       oo = outgoing;
02046       outgoing = outgoing->q_next;
02047       if (oo->member)
02048          ao2_ref(oo->member, -1);
02049       ast_free(oo);
02050    }
02051 }

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

References call_queue::announcefrequency, call_queue::announceholdtime, 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, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, penalty_rule::list, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_LINEAR, 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, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

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

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

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

Referenced by join_queue().

00616 {
00617    struct queue_ent *cur;
00618 
00619    if (!q || !new)
00620       return;
00621    if (prev) {
00622       cur = prev->next;
00623       prev->next = new;
00624    } else {
00625       cur = q->head;
00626       q->head = new;
00627    }
00628    new->next = cur;
00629    new->parent = q;
00630    new->pos = ++(*pos);
00631    new->opos = *pos;
00632 }

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

References ast_calloc, ast_free, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_strdupa, ast_strlen_zero(), rule_list::list, LOG_ERROR, LOG_WARNING, rule_list::name, and rule_list::rules.

Referenced by reload_queue_rules().

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

static char* int2strat ( int  strategy  )  [static]

Definition at line 545 of file app_queue.c.

References strategies.

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

00546 {
00547    int x;
00548 
00549    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00550       if (strategy == strategies[x].strategy)
00551          return strategies[x].name;
00552    }
00553 
00554    return "<unknown>";
00555 }

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

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

04010 {
04011    struct member *mem;
04012    struct ao2_iterator mem_iter;
04013 
04014    if (!q)
04015       return NULL;
04016 
04017    mem_iter = ao2_iterator_init(q->members, 0);
04018    while ((mem = ao2_iterator_next(&mem_iter))) {
04019       if (!strcasecmp(interface, mem->interface))
04020          return mem;
04021       ao2_ref(mem, -1);
04022    }
04023 
04024    return NULL;
04025 }

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

Definition at line 1020 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, queue_unref(), queues, and member::state_interface.

Referenced by remove_from_interfaces().

01021 {
01022    struct call_queue *q;
01023    struct member *mem, tmpmem;
01024    struct ao2_iterator queue_iter, mem_iter;
01025    int ret = 0;
01026 
01027    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01028    queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK);
01029    while ((q = ao2_iterator_next(&queue_iter))) {
01030       ao2_lock(q);
01031       mem_iter = ao2_iterator_init(q->members, 0);
01032       while ((mem = ao2_iterator_next(&mem_iter))) { 
01033          if (!strcasecmp(mem->state_interface, interface)) {
01034             ao2_ref(mem, -1);
01035             ret = 1;
01036             break;
01037          }
01038       }
01039       ao2_unlock(q);
01040       queue_unref(q);
01041    }
01042 
01043    return ret;
01044 }

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

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

Referenced by queue_exec(), and wait_our_turn().

02806 {
02807    struct queue_ent *ch;
02808    int res;
02809    int avl;
02810    int idx = 0;
02811    /* This needs a lock. How many members are available to be served? */
02812    ao2_lock(qe->parent);
02813 
02814    avl = num_available_members(qe->parent);
02815 
02816    ch = qe->parent->head;
02817 
02818    ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02819 
02820    while ((idx < avl) && (ch) && (ch != qe)) {
02821       if (!ch->pending)
02822          idx++;
02823       ch = ch->next;       
02824    }
02825 
02826    ao2_unlock(qe->parent);
02827 
02828    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02829    if (ch && idx < avl) {
02830       ast_debug(1, "It's our turn (%s).\n", qe->chan->name);
02831       res = 1;
02832    } else {
02833       ast_debug(1, "It's not our turn (%s).\n", qe->chan->name);
02834       res = 0;
02835    }
02836 
02837    return res;
02838 }

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

Definition at line 1707 of file app_queue.c.

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

Referenced by queue_exec().

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

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 1980 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, penalty_rule::list, manager_event, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, queue_ent::pos, queue_ent::qe_rules, queue_ref(), queue_unref(), queues, call_queue::realtime, ast_channel::uniqueid, and var.

Referenced by wait_our_turn().

01981 {
01982    struct call_queue *q;
01983    struct queue_ent *cur, *prev = NULL;
01984    struct penalty_rule *pr_iter;
01985    int pos = 0;
01986 
01987    if (!(q = qe->parent))
01988       return;
01989    queue_ref(q);
01990    ao2_lock(q);
01991 
01992    prev = NULL;
01993    for (cur = q->head; cur; cur = cur->next) {
01994       if (cur == qe) {
01995          q->count--;
01996 
01997          /* Take us out of the queue */
01998          manager_event(EVENT_FLAG_CALL, "Leave",
01999             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
02000             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
02001          ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
02002          /* Take us out of the queue */
02003          if (prev)
02004             prev->next = cur->next;
02005          else
02006             q->head = cur->next;
02007          /* Free penalty rules */
02008          while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list)))
02009             ast_free(pr_iter);
02010       } else {
02011          /* Renumber the people after us in the queue based on a new count */
02012          cur->pos = ++pos;
02013          prev = cur;
02014       }
02015    }
02016    ao2_unlock(q);
02017 
02018    /*If the queue is a realtime queue, check to see if it's still defined in real time*/
02019    if (q->realtime) {
02020       struct ast_variable *var;
02021       if (!(var = ast_load_realtime("queues", "name", q->name, NULL))) {
02022          q->dead = 1;
02023       } else {
02024          ast_variables_destroy(var);
02025       }
02026    }
02027 
02028    if (q->dead) { 
02029       /* It's dead and nobody is in it, so kill it */
02030       ao2_unlink(queues, q);
02031    }
02032    /* unref the explicit ref earlier in the function */
02033    queue_unref(q);
02034 }

static int load_module ( void   )  [static]

Definition at line 6646 of file app_queue.c.

References ao2_container_alloc(), aqm_exec(), ast_add_extension2(), ast_cli_register_multiple(), ast_cond_init(), 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_mutex_init(), ast_pthread_create, ast_register_application, ast_strdup, cli_queue, device_state, device_state_cb(), device_state_sub, device_state_thread(), EVENT_FLAG_AGENT, LOG_ERROR, 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(), rqm_exec(), and upqm_exec().

06647 {
06648    int res;
06649    struct ast_context *con;
06650 
06651    queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb);
06652 
06653    if (!reload_queues(0))
06654       return AST_MODULE_LOAD_DECLINE;
06655 
06656    con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue");
06657    if (!con)
06658       ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n");
06659    else
06660       ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue");
06661 
06662    if (queue_persistent_members)
06663       reload_queue_members();
06664 
06665    ast_mutex_init(&device_state.lock);
06666    ast_cond_init(&device_state.cond, NULL);
06667    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
06668 
06669    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06670    res = ast_register_application(app, queue_exec, synopsis, descrip);
06671    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
06672    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
06673    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
06674    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
06675    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
06676    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
06677    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
06678    res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary");
06679    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
06680    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
06681    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
06682    res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log");
06683    res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 
06684    res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules");
06685    res |= ast_custom_function_register(&queuevar_function);
06686    res |= ast_custom_function_register(&queuemembercount_function);
06687    res |= ast_custom_function_register(&queuemembercount_dep);
06688    res |= ast_custom_function_register(&queuememberlist_function);
06689    res |= ast_custom_function_register(&queuewaitingcount_function);
06690    res |= ast_custom_function_register(&queuememberpenalty_function);
06691    if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END)))
06692       res = -1;
06693 
06694    return res ? AST_MODULE_LOAD_DECLINE : 0;
06695 }

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

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

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

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

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

Definition at line 6021 of file app_queue.c.

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

Referenced by load_module().

06022 {
06023    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
06024    int paused, penalty = 0;
06025 
06026    queuename = astman_get_header(m, "Queue");
06027    interface = astman_get_header(m, "Interface");
06028    penalty_s = astman_get_header(m, "Penalty");
06029    paused_s = astman_get_header(m, "Paused");
06030    membername = astman_get_header(m, "MemberName");
06031    state_interface = astman_get_header(m, "StateInterface");
06032 
06033    if (ast_strlen_zero(queuename)) {
06034       astman_send_error(s, m, "'Queue' not specified.");
06035       return 0;
06036    }
06037 
06038    if (ast_strlen_zero(interface)) {
06039       astman_send_error(s, m, "'Interface' not specified.");
06040       return 0;
06041    }
06042 
06043    if (ast_strlen_zero(penalty_s))
06044       penalty = 0;
06045    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
06046       penalty = 0;
06047 
06048    if (ast_strlen_zero(paused_s))
06049       paused = 0;
06050    else
06051       paused = abs(ast_true(paused_s));
06052 
06053    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
06054    case RES_OKAY:
06055       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
06056       astman_send_ack(s, m, "Added interface to queue");
06057       break;
06058    case RES_EXISTS:
06059       astman_send_error(s, m, "Unable to add interface: Already there");
06060       break;
06061    case RES_NOSUCHQUEUE:
06062       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
06063       break;
06064    case RES_OUTOFMEMORY:
06065       astman_send_error(s, m, "Out of memory");
06066       break;
06067    }
06068 
06069    return 0;
06070 }

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

Definition at line 6106 of file app_queue.c.

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

Referenced by load_module().

06107 {
06108    const char *queuename, *interface, *paused_s, *reason;
06109    int paused;
06110 
06111    interface = astman_get_header(m, "Interface");
06112    paused_s = astman_get_header(m, "Paused");
06113    queuename = astman_get_header(m, "Queue");      /* Optional - if not supplied, pause the given Interface in all queues */
06114    reason = astman_get_header(m, "Reason");        /* Optional - Only used for logging purposes */
06115 
06116    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
06117       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
06118       return 0;
06119    }
06120 
06121    paused = abs(ast_true(paused_s));
06122 
06123    if (set_member_paused(queuename, interface, reason, paused))
06124       astman_send_error(s, m, "Interface not found");
06125    else
06126       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
06127    return 0;
06128 }

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

Definition at line 6130 of file app_queue.c.

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

Referenced by load_module().

06131 {
06132    const char *queuename, *event, *message, *interface, *uniqueid;
06133 
06134    queuename = astman_get_header(m, "Queue");
06135    uniqueid = astman_get_header(m, "UniqueId");
06136    interface = astman_get_header(m, "Interface");
06137    event = astman_get_header(m, "Event");
06138    message = astman_get_header(m, "Message");
06139 
06140    if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) {
06141       astman_send_error(s, m, "Need 'Queue' and 'Event' parameters.");
06142       return 0;
06143    }
06144 
06145    ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message);
06146    astman_send_ack(s, m, "Event added successfully");
06147 
06148    return 0;
06149 }

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

Definition at line 6182 of file app_queue.c.

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

Referenced by load_module().

06183 {
06184    const char *queuename, *interface, *penalty_s;
06185    int penalty;
06186 
06187    interface = astman_get_header(m, "Interface");
06188    penalty_s = astman_get_header(m, "Penalty");
06189    /* Optional - if not supplied, set the penalty value for the given Interface in all queues */
06190    queuename = astman_get_header(m, "Queue");
06191 
06192    if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) {
06193       astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters.");
06194       return 0;
06195    }
06196  
06197    penalty = atoi(penalty_s);
06198 
06199    if (set_member_penalty((char *)queuename, (char *)interface, penalty))
06200       astman_send_error(s, m, "Invalid interface, queuename or penalty");
06201    else
06202       astman_send_ack(s, m, "Interface penalty set successfully");
06203 
06204    return 0;
06205 }

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

Definition at line 5831 of file app_queue.c.

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

Referenced by load_module().

05832 {
05833    const char *rule = astman_get_header(m, "Rule");
05834    struct rule_list *rl_iter;
05835    struct penalty_rule *pr_iter;
05836 
05837    AST_LIST_LOCK(&rule_lists);
05838    AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) {
05839       if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) {
05840          astman_append(s, "RuleList: %s\r\n", rl_iter->name);
05841          AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) {
05842             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 );
05843          }
05844          if (!ast_strlen_zero(rule))
05845             break;
05846       }
05847    }
05848    AST_LIST_UNLOCK(&rule_lists);
05849 
05850    astman_append(s, "\r\n\r\n");
05851 
05852    return RESULT_SUCCESS;
05853 }

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

Definition at line 5821 of file app_queue.c.

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

Referenced by load_module().

05822 {
05823    char *a[] = { "queue", "show" };
05824 
05825    __queues_show(s, -1, 2, a);
05826    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05827 
05828    return RESULT_SUCCESS;
05829 }

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

Queue status info via AMI.

Definition at line 5928 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, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, member::dynamic, int2strat(), member::interface, member::lastcall, member::membername, ast_channel::name, queue_ent::next, member::paused, member::penalty, queue_unref(), queues, RESULT_SUCCESS, s, S_OR, queue_ent::start, and member::status.

Referenced by load_module().

05929 {
05930    time_t now;
05931    int pos;
05932    const char *id = astman_get_header(m,"ActionID");
05933    const char *queuefilter = astman_get_header(m,"Queue");
05934    const char *memberfilter = astman_get_header(m,"Member");
05935    char idText[256] = "";
05936    struct call_queue *q;
05937    struct queue_ent *qe;
05938    float sl = 0;
05939    struct member *mem;
05940    struct ao2_iterator queue_iter;
05941    struct ao2_iterator mem_iter;
05942 
05943    astman_send_ack(s, m, "Queue status will follow");
05944    time(&now);
05945    if (!ast_strlen_zero(id))
05946       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05947 
05948    queue_iter = ao2_iterator_init(queues, 0);
05949    while ((q = ao2_iterator_next(&queue_iter))) {
05950       ao2_lock(q);
05951 
05952       /* List queue properties */
05953       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05954          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05955          astman_append(s, "Event: QueueParams\r\n"
05956             "Queue: %s\r\n"
05957             "Max: %d\r\n"
05958             "Strategy: %s\r\n"
05959             "Calls: %d\r\n"
05960             "Holdtime: %d\r\n"
05961             "Completed: %d\r\n"
05962             "Abandoned: %d\r\n"
05963             "ServiceLevel: %d\r\n"
05964             "ServicelevelPerf: %2.1f\r\n"
05965             "Weight: %d\r\n"
05966             "%s"
05967             "\r\n",
05968             q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted,
05969             q->callsabandoned, q->servicelevel, sl, q->weight, idText);
05970          /* List Queue Members */
05971          mem_iter = ao2_iterator_init(q->members, 0);
05972          while ((mem = ao2_iterator_next(&mem_iter))) {
05973             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) {
05974                astman_append(s, "Event: QueueMember\r\n"
05975                   "Queue: %s\r\n"
05976                   "Name: %s\r\n"
05977                   "Location: %s\r\n"
05978                   "Membership: %s\r\n"
05979                   "Penalty: %d\r\n"
05980                   "CallsTaken: %d\r\n"
05981                   "LastCall: %d\r\n"
05982                   "Status: %d\r\n"
05983                   "Paused: %d\r\n"
05984                   "%s"
05985                   "\r\n",
05986                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05987                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05988             }
05989             ao2_ref(mem, -1);
05990          }
05991          /* List Queue Entries */
05992          pos = 1;
05993          for (qe = q->head; qe; qe = qe->next) {
05994             astman_append(s, "Event: QueueEntry\r\n"
05995                "Queue: %s\r\n"
05996                "Position: %d\r\n"
05997                "Channel: %s\r\n"
05998                "CallerIDNum: %s\r\n"
05999                "CallerIDName: %s\r\n"
06000                "Wait: %ld\r\n"
06001                "%s"
06002                "\r\n",
06003                q->name, pos++, qe->chan->name,
06004                S_OR(qe->chan->cid.cid_num, "unknown"),
06005                S_OR(qe->chan->cid.cid_name, "unknown"),
06006                (long) (now - qe->start), idText);
06007          }
06008       }
06009       ao2_unlock(q);
06010       queue_unref(q);
06011    }
06012 
06013    astman_append(s,
06014       "Event: QueueStatusComplete\r\n"
06015       "%s"
06016       "\r\n",idText);
06017 
06018    return RESULT_SUCCESS;
06019 }

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

Summary of queue info via the AMI.

Definition at line 5856 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(), queue_ent::next, member::paused, queue_unref(), queues, RESULT_SUCCESS, s, queue_ent::start, and member::status.

Referenced by load_module().

05857 {
05858    time_t now;
05859    int qmemcount = 0;
05860    int qmemavail = 0;
05861    int qchancount = 0;
05862    int qlongestholdtime = 0;
05863    const char *id = astman_get_header(m, "ActionID");
05864    const char *queuefilter = astman_get_header(m, "Queue");
05865    char idText[256] = "";
05866    struct call_queue *q;
05867    struct queue_ent *qe;
05868    struct member *mem;
05869    struct ao2_iterator queue_iter;
05870    struct ao2_iterator mem_iter;
05871 
05872    astman_send_ack(s, m, "Queue summary will follow");
05873    time(&now);
05874    if (!ast_strlen_zero(id))
05875       snprintf(idText, 256, "ActionID: %s\r\n", id);
05876    queue_iter = ao2_iterator_init(queues, 0);
05877    while ((q = ao2_iterator_next(&queue_iter))) {
05878       ao2_lock(q);
05879 
05880       /* List queue properties */
05881       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05882          /* Reset the necessary local variables if no queuefilter is set*/
05883          qmemcount = 0;
05884          qmemavail = 0;
05885          qchancount = 0;
05886          qlongestholdtime = 0;
05887 
05888          /* List Queue Members */
05889          mem_iter = ao2_iterator_init(q->members, 0);
05890          while ((mem = ao2_iterator_next(&mem_iter))) {
05891             if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) {
05892                ++qmemcount;
05893                if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) {
05894                   ++qmemavail;
05895                }
05896             }
05897             ao2_ref(mem, -1);
05898          }
05899          for (qe = q->head; qe; qe = qe->next) {
05900             if ((now - qe->start) > qlongestholdtime) {
05901                qlongestholdtime = now - qe->start;
05902             }
05903             ++qchancount;
05904          }
05905          astman_append(s, "Event: QueueSummary\r\n"
05906             "Queue: %s\r\n"
05907             "LoggedIn: %d\r\n"
05908             "Available: %d\r\n"
05909             "Callers: %d\r\n" 
05910             "HoldTime: %d\r\n"
05911             "LongestHoldTime: %d\r\n"
05912             "%s"
05913             "\r\n",
05914             q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText);
05915       }
05916       ao2_unlock(q);
05917       queue_unref(q);
05918    }
05919    astman_append(s,
05920       "Event: QueueSummaryComplete\r\n"
05921       "%s"
05922       "\r\n", idText);
05923 
05924    return RESULT_SUCCESS;
05925 }

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

Definition at line 6072 of file app_queue.c.

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

Referenced by load_module().

06073 {
06074    const char *queuename, *interface;
06075 
06076    queuename = astman_get_header(m, "Queue");
06077    interface = astman_get_header(m, "Interface");
06078 
06079    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
06080       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
06081       return 0;
06082    }
06083 
06084    switch (remove_from_queue(queuename, interface)) {
06085    case RES_OKAY:
06086       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
06087       astman_send_ack(s, m, "Removed interface from queue");
06088       break;
06089    case RES_EXISTS:
06090       astman_send_error(s, m, "Unable to remove interface: Not there");
06091       break;
06092    case RES_NOSUCHQUEUE:
06093       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
06094       break;
06095    case RES_OUTOFMEMORY:
06096       astman_send_error(s, m, "Out of memory");
06097       break;
06098    case RES_NOT_DYNAMIC:
06099       astman_send_error(s, m, "Member not dynamic");
06100       break;
06101    }
06102 
06103    return 0;
06104 }

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

Definition at line 911 of file app_queue.c.

References CMP_MATCH, and member::interface.

Referenced by init_queue().

00912 {
00913    struct member *mem1 = obj1, *mem2 = obj2;
00914    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00915 }

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

Definition at line 899 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00900 {
00901    const struct member *mem = obj;
00902    const char *chname = strchr(mem->interface, '/');
00903    int ret = 0, i;
00904    if (!chname)
00905       chname = mem->interface;
00906    for (i = 0; i < 5 && chname[i]; i++)
00907       ret += compress_char(chname[i]) << (i * 6);
00908    return ret;
00909 }

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

02062 {
02063    struct member *mem;
02064    int avl = 0;
02065    struct ao2_iterator mem_iter;
02066 
02067    mem_iter = ao2_iterator_init(q->members, 0);
02068    while ((mem = ao2_iterator_next(&mem_iter))) {
02069       switch (mem->status) {
02070       case AST_DEVICE_INUSE:
02071          if (!q->ringinuse)
02072             break;
02073          /* else fall through */
02074       case AST_DEVICE_NOT_INUSE:
02075       case AST_DEVICE_UNKNOWN:
02076          if (!mem->paused) {
02077             avl++;
02078          }
02079          break;
02080       }
02081       ao2_ref(mem, -1);
02082 
02083       /* If autofill is not enabled or if the queue's strategy is ringall, then
02084        * we really don't care about the number of available members so much as we
02085        * do that there is at least one available.
02086        *
02087        * In fact, we purposely will return from this function stating that only
02088        * one member is available if either of those conditions hold. That way,
02089        * functions which determine what action to take based on the number of available
02090        * members will operate properly. The reasoning is that even if multiple
02091        * members are available, only the head caller can actually be serviced.
02092        */
02093       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
02094          break;
02095       }
02096    }
02097 
02098    return avl;
02099 }

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

Definition at line 1787 of file app_queue.c.

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

Referenced by say_periodic_announcement(), and say_position().

01788 {
01789    int res;
01790 
01791    if (ast_strlen_zero(filename)) {
01792       return 0;
01793    }
01794 
01795    ast_stopstream(chan);
01796 
01797    res = ast_streamfile(chan, filename, chan->language);
01798    if (!res)
01799       res = ast_waitstream(chan, AST_DIGIT_ANY);
01800 
01801    ast_stopstream(chan);
01802 
01803    return res;
01804 }

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

PauseQueueMember application.

Definition at line 4432 of file app_queue.c.

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

Referenced by load_module().

04433 {
04434    char *parse;
04435    AST_DECLARE_APP_ARGS(args,
04436       AST_APP_ARG(queuename);
04437       AST_APP_ARG(interface);
04438       AST_APP_ARG(options);
04439       AST_APP_ARG(reason);
04440    );
04441 
04442    if (ast_strlen_zero(data)) {
04443       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n");
04444       return -1;
04445    }
04446 
04447    parse = ast_strdupa(data);
04448 
04449    AST_STANDARD_APP_ARGS(args, parse);
04450 
04451    if (ast_strlen_zero(args.interface)) {
04452       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04453       return -1;
04454    }
04455 
04456    if (set_member_paused(args.queuename, args.interface, args.reason, 1)) {
04457       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
04458       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
04459       return 0;
04460    }
04461 
04462    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
04463 
04464    return 0;
04465 }

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

QueueLog application.

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

04623 {
04624    char *parse;
04625 
04626    AST_DECLARE_APP_ARGS(args,
04627       AST_APP_ARG(queuename);
04628       AST_APP_ARG(uniqueid);
04629       AST_APP_ARG(membername);
04630       AST_APP_ARG(event);
04631       AST_APP_ARG(params);
04632    );
04633 
04634    if (ast_strlen_zero(data)) {
04635       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n");
04636       return -1;
04637    }
04638 
04639    parse = ast_strdupa(data);
04640 
04641    AST_STANDARD_APP_ARGS(args, parse);
04642 
04643    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04644        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04645       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n");
04646       return -1;
04647    }
04648 
04649    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04650       "%s", args.params ? args.params : "");
04651 
04652    return 0;
04653 }

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

Definition at line 576 of file app_queue.c.

References CMP_MATCH, and call_queue::name.

Referenced by load_module().

00577 {
00578    struct call_queue *q = obj, *q2 = arg;
00579    return !strcasecmp(q->name, q2->name) ? CMP_MATCH : 0;
00580 }

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

References AST_APP_ARG, ast_cdr_noanswer(), AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_moh_start(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, chan, ast_channel::cid, ast_callerid::cid_num, get_member_status(), is_our_turn(), join_queue(), LOG_WARNING, ast_channel::name, parse(), pbx_builtin_getvar_helper(), QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), stop, try_calling(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04701 {
04702    int res=-1;
04703    int ringing=0;
04704    const char *user_priority;
04705    const char *max_penalty_str;
04706    const char *min_penalty_str;
04707    int prio;
04708    int qcontinue = 0;
04709    int max_penalty, min_penalty;
04710    enum queue_result reason = QUEUE_UNKNOWN;
04711    /* whether to exit Queue application after the timeout hits */
04712    int tries = 0;
04713    int noption = 0;
04714    char *parse;
04715    int makeannouncement = 0;
04716    AST_DECLARE_APP_ARGS(args,
04717       AST_APP_ARG(queuename);
04718       AST_APP_ARG(options);
04719       AST_APP_ARG(url);
04720       AST_APP_ARG(announceoverride);
04721       AST_APP_ARG(queuetimeoutstr);
04722       AST_APP_ARG(agi);
04723       AST_APP_ARG(macro);
04724       AST_APP_ARG(gosub);
04725       AST_APP_ARG(rule);
04726    );
04727    /* Our queue entry */
04728    struct queue_ent qe;
04729    
04730    if (ast_strlen_zero(data)) {
04731       ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n");
04732       return -1;
04733    }
04734    
04735    parse = ast_strdupa(data);
04736    AST_STANDARD_APP_ARGS(args, parse);
04737 
04738    /* Setup our queue entry */
04739    memset(&qe, 0, sizeof(qe));
04740    qe.start = time(NULL);
04741 
04742    /* set the expire time based on the supplied timeout; */
04743    if (!ast_strlen_zero(args.queuetimeoutstr))
04744       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04745    else
04746       qe.expire = 0;
04747 
04748    /* Get the priority from the variable ${QUEUE_PRIO} */
04749    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04750    if (user_priority) {
04751       if (sscanf(user_priority, "%d", &prio) == 1) {
04752          ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio);
04753       } else {
04754          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04755             user_priority, chan->name);
04756          prio = 0;
04757       }
04758    } else {
04759       ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n");
04760       prio = 0;
04761    }
04762 
04763    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04764 
04765    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04766       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
04767          ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty);
04768       } else {
04769          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04770             max_penalty_str, chan->name);
04771          max_penalty = 0;
04772       }
04773    } else {
04774       max_penalty = 0;
04775    }
04776 
04777    if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) {
04778       if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) {
04779          ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty);
04780       } else {
04781          ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n",
04782             min_penalty_str, chan->name);
04783          min_penalty = 0;
04784       }
04785    } else {
04786       min_penalty = 0;
04787    }
04788 
04789    if (args.options && (strchr(args.options, 'r')))
04790       ringing = 1;
04791 
04792    if (args.options && (strchr(args.options, 'c')))
04793       qcontinue = 1;
04794 
04795    ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04796       args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04797 
04798    qe.chan = chan;
04799    qe.prio = prio;
04800    qe.max_penalty = max_penalty;
04801    qe.min_penalty = min_penalty;
04802    qe.last_pos_said = 0;
04803    qe.last_pos = 0;
04804    qe.last_periodic_announce_time = time(NULL);
04805    qe.last_periodic_announce_sound = 0;
04806    qe.valid_digits = 0;
04807    if (join_queue(args.queuename, &qe, &reason, args.rule)) {
04808       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04809       set_queue_result(chan, reason);
04810       return 0;
04811    }
04812    ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04813       S_OR(chan->cid.cid_num, ""));
04814 check_turns:
04815    if (ringing) {
04816       ast_indicate(chan, AST_CONTROL_RINGING);
04817    } else {
04818       ast_moh_start(chan, qe.moh, NULL);
04819    }
04820 
04821    /* This is the wait loop for callers 2 through maxlen */
04822    res = wait_our_turn(&qe, ringing, &reason);
04823    if (res) {
04824       goto stop;
04825    }
04826 
04827    makeannouncement = 0;
04828 
04829    for (;;) {
04830       /* This is the wait loop for the head caller*/
04831       /* To exit, they may get their call answered; */
04832       /* they may dial a digit from the queue context; */
04833       /* or, they may timeout. */
04834 
04835       enum queue_member_status stat = QUEUE_NORMAL;
04836       int exit = 0;
04837 
04838       /* Leave if we have exceeded our queuetimeout */
04839       if (qe.expire && (time(NULL) >= qe.expire)) {
04840          record_abandoned(&qe);
04841          ast_cdr_noanswer(qe.chan->cdr);
04842          reason = QUEUE_TIMEOUT;
04843          res = 0;
04844          ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 
04845             qe.pos, qe.opos, (long) time(NULL) - qe.start);
04846          break;
04847       }
04848 
04849       if (makeannouncement) {
04850          /* Make a position announcement, if enabled */
04851          if (qe.parent->announcefrequency)
04852             if ((res = say_position(&qe,ringing)))
04853                goto stop;
04854       }
04855       makeannouncement = 1;
04856 
04857       /* Make a periodic announcement, if enabled */
04858       if (qe.parent->periodicannouncefrequency)
04859          if ((res = say_periodic_announcement(&qe,ringing)))
04860             goto stop;
04861    
04862       /* Leave if we have exceeded our queuetimeout */
04863       if (qe.expire && (time(NULL) >= qe.expire)) {
04864          record_abandoned(&qe);
04865          ast_cdr_noanswer(qe.chan->cdr);
04866          reason = QUEUE_TIMEOUT;
04867          res = 0;
04868          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04869          break;
04870       }
04871 
04872       /* see if we need to move to the next penalty level for this queue */
04873       while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) {
04874          update_qe_rule(&qe);
04875       }
04876 
04877       /* Try calling all queue members for 'timeout' seconds */
04878       res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing);
04879       if (res) {
04880          goto stop;
04881       }
04882 
04883       /* exit after 'timeout' cycle if 'n' option enabled */
04884       if (noption && tries >= qe.parent->membercount) {
04885          ast_verb(3, "Exiting on time-out cycle\n");
04886          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04887          record_abandoned(&qe);
04888          ast_cdr_noanswer(qe.chan->cdr);
04889          reason = QUEUE_TIMEOUT;
04890          res = 0;
04891          break;
04892       }
04893 
04894       for (; !exit || qe.pr; update_qe_rule(&qe)) {
04895          stat = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty);
04896 
04897          if (!qe.pr || stat == QUEUE_NORMAL) {
04898             break;
04899          }
04900 
04901          if ((qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) ||
04902                ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
04903                ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))) {
04904             continue;
04905          } else {
04906             exit = 1;
04907          }
04908       }
04909 
04910       /* leave the queue if no agents, if enabled */
04911       if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04912          record_abandoned(&qe);
04913          reason = QUEUE_LEAVEEMPTY;
04914          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04915          res = 0;
04916          break;
04917       }
04918 
04919       /* leave the queue if no reachable agents, if enabled */
04920       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
04921          record_abandoned(&qe);
04922          reason = QUEUE_LEAVEUNAVAIL;
04923          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04924          res = 0;
04925          break;
04926       }
04927       if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04928          record_abandoned(&qe);
04929          reason = QUEUE_LEAVEUNAVAIL;
04930          res = 0;
04931          break;
04932       }
04933 
04934       /* Leave if we have exceeded our queuetimeout */
04935       if (qe.expire && (time(NULL) >= qe.expire)) {
04936          record_abandoned(&qe);
04937          ast_cdr_noanswer(qe.chan->cdr);
04938          reason = QUEUE_TIMEOUT;
04939          res = 0;
04940          ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start);
04941          break;
04942       }
04943 
04944       /* If using dynamic realtime members, we should regenerate the member list for this queue */
04945       update_realtime_members(qe.parent);
04946       /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04947       res = wait_a_bit(&qe);
04948       if (res)
04949          goto stop;
04950 
04951       /* Since this is a priority queue and
04952        * it is not sure that we are still at the head
04953        * of the queue, go and check for our turn again.
04954        */
04955       if (!is_our_turn(&qe)) {
04956          ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name);
04957          goto check_turns;
04958       }
04959    }
04960 
04961 stop:
04962    if (res) {
04963       if (res < 0) {
04964          if (!qe.handled) {
04965             record_abandoned(&qe);
04966             ast_cdr_noanswer(qe.chan->cdr);
04967             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04968                "%d|%d|%ld", qe.pos, qe.opos,
04969                (long) time(NULL) - qe.start);
04970             res = -1;
04971          } else if (qcontinue) {
04972             reason = QUEUE_CONTINUE;
04973             res = 0;
04974          }
04975       } else if (qe.valid_digits) {
04976          ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04977             "%s|%d", qe.digits, qe.pos);
04978       }
04979    }
04980 
04981    /* Don't allow return code > 0 */
04982    if (res >= 0) {
04983       res = 0; 
04984       if (ringing) {
04985          ast_indicate(chan, -1);
04986       } else {
04987          ast_moh_stop(chan);
04988       }        
04989       ast_stopstream(chan);
04990    }
04991 
04992    set_queue_variables(qe.parent, qe.chan);
04993 
04994    leave_queue(&qe);
04995    if (reason != QUEUE_UNKNOWN)
04996       set_queue_result(chan, reason);
04997 
04998    return res;
04999 }

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

05230 {
05231    int penalty;
05232    AST_DECLARE_APP_ARGS(args,
05233       AST_APP_ARG(queuename);
05234       AST_APP_ARG(interface);
05235         );
05236    /* Make sure the returned value on error is NULL. */
05237    buf[0] = '\0';
05238 
05239    if (ast_strlen_zero(data)) {
05240       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05241       return -1;
05242    }
05243 
05244    AST_STANDARD_APP_ARGS(args, data);
05245 
05246    if (args.argc < 2) {
05247       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05248       return -1;
05249    }
05250 
05251    penalty = get_member_penalty (args.queuename, args.interface);
05252    
05253    if (penalty >= 0) /* remember that buf is already '\0' */
05254       snprintf (buf, len, "%d", penalty);
05255 
05256    return 0;
05257 }

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

05261 {
05262    int penalty;
05263    AST_DECLARE_APP_ARGS(args,
05264       AST_APP_ARG(queuename);
05265       AST_APP_ARG(interface);
05266    );
05267 
05268    if (ast_strlen_zero(data)) {
05269       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05270       return -1;
05271    }
05272 
05273    AST_STANDARD_APP_ARGS(args, data);
05274 
05275    if (args.argc < 2) {
05276       ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n");
05277       return -1;
05278    }
05279 
05280    penalty = atoi(value);
05281 
05282    if (ast_strlen_zero(args.interface)) {
05283       ast_log (LOG_ERROR, "<interface> parameter can't be null\n");
05284       return -1;
05285    }
05286 
05287    /* if queuename = NULL then penalty will be set for interface in all the queues. */
05288    if (set_member_penalty(args.queuename, args.interface, penalty)) {
05289       ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n");
05290       return -1;
05291    }
05292 
05293    return 0;
05294 }

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 5052 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(), load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_unref(), and member::status.

05053 {
05054    int count = 0;
05055    struct member *m;
05056    struct ao2_iterator mem_iter;
05057    struct call_queue *q;
05058    char *option;
05059 
05060    if (ast_strlen_zero(data)) {
05061       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05062       return -1;
05063    }
05064 
05065    if ((option = strchr(data, ',')))
05066       *option++ = '\0';
05067    else
05068       option = "logged";
05069    if ((q = load_realtime_queue(data))) {
05070       ao2_lock(q);
05071       if (!strcasecmp(option, "logged")) {
05072          mem_iter = ao2_iterator_init(q->members, 0);
05073          while ((m = ao2_iterator_next(&mem_iter))) {
05074             /* Count the agents who are logged in and presently answering calls */
05075             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05076                count++;
05077             }
05078             ao2_ref(m, -1);
05079          }
05080       } else if (!strcasecmp(option, "free")) {
05081          mem_iter = ao2_iterator_init(q->members, 0);
05082          while ((m = ao2_iterator_next(&mem_iter))) {
05083             /* Count the agents who are logged in and presently answering calls */
05084             if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) {
05085                count++;
05086             }
05087             ao2_ref(m, -1);
05088          }
05089       } else /* must be "count" */
05090          count = q->membercount;
05091       ao2_unlock(q);
05092       queue_unref(q);
05093    } else
05094       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05095 
05096    snprintf(buf, len, "%d", count);
05097 
05098    return 0;
05099 }

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 5106 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(), load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, queue_unref(), and member::status.

05107 {
05108    int count = 0;
05109    struct member *m;
05110    struct call_queue *q;
05111    struct ao2_iterator mem_iter;
05112    static int depflag = 1;
05113 
05114    if (depflag) {
05115       depflag = 0;
05116       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");
05117    }
05118 
05119    if (ast_strlen_zero(data)) {
05120       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05121       return -1;
05122    }
05123    
05124    if ((q = load_realtime_queue(data))) {
05125       ao2_lock(q);
05126       mem_iter = ao2_iterator_init(q->members, 0);
05127       while ((m = ao2_iterator_next(&mem_iter))) {
05128          /* Count the agents who are logged in and presently answering calls */
05129          if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05130             count++;
05131          }
05132          ao2_ref(m, -1);
05133       }
05134       ao2_unlock(q);
05135       queue_unref(q);
05136    } else
05137       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05138 
05139    snprintf(buf, len, "%d", count);
05140 
05141    return 0;
05142 }

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 5181 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(), LOG_ERROR, LOG_WARNING, member::membername, call_queue::members, OBJ_POINTER, queue_unref(), and queues.

05182 {
05183    struct call_queue *q, tmpq = {
05184       .name = data,  
05185    };
05186    struct member *m;
05187 
05188    /* Ensure an otherwise empty list doesn't return garbage */
05189    buf[0] = '\0';
05190 
05191    if (ast_strlen_zero(data)) {
05192       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
05193       return -1;
05194    }
05195 
05196    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05197       int buflen = 0, count = 0;
05198       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
05199 
05200       ao2_lock(q);
05201       while ((m = ao2_iterator_next(&mem_iter))) {
05202          /* strcat() is always faster than printf() */
05203          if (count++) {
05204             strncat(buf + buflen, ",", len - buflen - 1);
05205             buflen++;
05206          }
05207          strncat(buf + buflen, m->membername, len - buflen - 1);
05208          buflen += strlen(m->membername);
05209          /* Safeguard against overflow (negative length) */
05210          if (buflen >= len - 2) {
05211             ao2_ref(m, -1);
05212             ast_log(LOG_WARNING, "Truncating list\n");
05213             break;
05214          }
05215          ao2_ref(m, -1);
05216       }
05217       ao2_unlock(q);
05218       queue_unref(q);
05219    } else
05220       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05221 
05222    /* We should already be terminated, but let's make sure. */
05223    buf[len - 1] = '\0';
05224 
05225    return 0;
05226 }

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

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

05146 {
05147    int count = 0;
05148    struct call_queue *q, tmpq = {
05149       .name = data,  
05150    };
05151    struct ast_variable *var = NULL;
05152 
05153    buf[0] = '\0';
05154    
05155    if (ast_strlen_zero(data)) {
05156       ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n");
05157       return -1;
05158    }
05159 
05160    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05161       ao2_lock(q);
05162       count = q->count;
05163       ao2_unlock(q);
05164       queue_unref(q);
05165    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
05166       /* if the queue is realtime but was not found in memory, this
05167        * means that the queue had been deleted from memory since it was 
05168        * "dead." This means it has a 0 waiting count
05169        */
05170       count = 0;
05171       ast_variables_destroy(var);
05172    } else
05173       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05174 
05175    snprintf(buf, len, "%d", count);
05176 
05177    return 0;
05178 }

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

References ao2_find(), ao2_lock(), ao2_unlock(), ast_log(), ast_strlen_zero(), chan, int2strat(), LOG_ERROR, LOG_WARNING, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_unref(), and queues.

05007 {
05008    int res = -1;
05009    struct call_queue *q, tmpq = {
05010       .name = data,  
05011    };
05012 
05013    char interfacevar[256]="";
05014         float sl = 0;
05015 
05016    if (ast_strlen_zero(data)) {
05017       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
05018       return -1;
05019    }
05020 
05021    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05022       ao2_lock(q);
05023          if (q->setqueuevar) {
05024               sl = 0;
05025          res = 0;
05026 
05027               if (q->callscompleted > 0)
05028                       sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05029 
05030               snprintf(interfacevar, sizeof(interfacevar),
05031                      "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
05032                       q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
05033 
05034               pbx_builtin_setvar_multiple(chan, interfacevar);
05035            }
05036 
05037       ao2_unlock(q);
05038       queue_unref(q);
05039    } else
05040       ast_log(LOG_WARNING, "queue %s was not found\n", data);
05041 
05042    snprintf(buf, len, "%d", res);
05043 
05044    return 0;
05045 }

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

Definition at line 569 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

00570 {
00571    const struct call_queue *q = obj;
00572 
00573    return ast_str_case_hash(q->name);
00574 }

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

Definition at line 582 of file app_queue.c.

References ao2_ref().

Referenced by leave_queue().

00583 {
00584    ao2_ref(q, 1);
00585    return q;
00586 }

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

References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, buf, queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, 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, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

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

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

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

05803 {
05804    switch ( cmd ) {
05805    case CLI_INIT:
05806       e->command = "queue show";
05807       e->usage =
05808          "Usage: queue show\n"
05809          "       Provides summary information on a specified queue.\n";
05810       return NULL;
05811    case CLI_GENERATE:
05812       return complete_queue_show(a->line, a->word, a->pos, a->n); 
05813    }
05814 
05815    return __queues_show(NULL, a->fd, a->argc, a->argv);
05816 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3136 of file app_queue.c.

References ast_free.

03137 {
03138    struct queue_transfer_ds *qtds = data;
03139    ast_free(qtds);
03140 }

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 3159 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::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, ast_channel::uniqueid, and update_queue().

03160 {
03161    struct queue_transfer_ds *qtds = data;
03162    struct queue_ent *qe = qtds->qe;
03163    struct member *member = qtds->member;
03164    time_t callstart = qtds->starttime;
03165    int callcompletedinsl = qtds->callcompletedinsl;
03166    struct ast_datastore *datastore;
03167 
03168    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03169             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
03170             (long) (time(NULL) - callstart));
03171 
03172    update_queue(qe->parent, member, callcompletedinsl);
03173    
03174    /* No need to lock the channels because they are already locked in ast_do_masquerade */
03175    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
03176       ast_channel_datastore_remove(old_chan, datastore);
03177    } else {
03178       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
03179    }
03180 }

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

Definition at line 588 of file app_queue.c.

References ao2_ref().

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

00589 {
00590    ao2_ref(q, -1);
00591    return q;
00592 }

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

Definition at line 1961 of file app_queue.c.

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

01962 {
01963    int oldvalue;
01964 
01965    /* Calculate holdtime using an exponential average */
01966    /* Thanks to SRT for this contribution */
01967    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01968 
01969    ao2_lock(qe->parent);
01970    oldvalue = qe->parent->holdtime;
01971    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01972    ao2_unlock(qe->parent);
01973 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 2507 of file app_queue.c.

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

Referenced by queue_exec().

02508 {
02509    ao2_lock(qe->parent);
02510    set_queue_variables(qe->parent, qe->chan);
02511    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02512       "Queue: %s\r\n"
02513       "Uniqueid: %s\r\n"
02514       "Position: %d\r\n"
02515       "OriginalPosition: %d\r\n"
02516       "HoldTime: %d\r\n",
02517       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02518 
02519    qe->parent->callsabandoned++;
02520    ao2_unlock(qe->parent);
02521 }

static int reload ( void   )  [static]

Definition at line 6697 of file app_queue.c.

References reload_queues().

06698 {
06699    reload_queues(1);
06700    return 0;
06701 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 4335 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(), errno, member::interface, ast_db_entry::key, load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_unref(), queues, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

04336 {
04337    char *cur_ptr;
04338    const char *queue_name;
04339    char *member;
04340    char *interface;
04341    char *membername = NULL;
04342    char *state_interface;
04343    char *penalty_tok;
04344    int penalty = 0;
04345    char *paused_tok;
04346    int paused = 0;
04347    struct ast_db_entry *db_tree;
04348    struct ast_db_entry *entry;
04349    struct call_queue *cur_queue;
04350    char queue_data[PM_MAX_LEN];
04351 
04352    ao2_lock(queues);
04353 
04354    /* Each key in 'pm_family' is the name of a queue */
04355    db_tree = ast_db_gettree(pm_family, NULL);
04356    for (entry = db_tree; entry; entry = entry->next) {
04357 
04358       queue_name = entry->key + strlen(pm_family) + 2;
04359 
04360       {
04361          struct call_queue tmpq = {
04362             .name = queue_name,
04363          };
04364          cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER);
04365       }  
04366 
04367       if (!cur_queue)
04368          cur_queue = load_realtime_queue(queue_name);
04369 
04370       if (!cur_queue) {
04371          /* If the queue no longer exists, remove it from the
04372           * database */
04373          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
04374          ast_db_del(pm_family, queue_name);
04375          continue;
04376       } 
04377 
04378       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) {
04379          queue_unref(cur_queue);
04380          continue;
04381       }
04382 
04383       cur_ptr = queue_data;
04384       while ((member = strsep(&cur_ptr, ",|"))) {
04385          if (ast_strlen_zero(member))
04386             continue;
04387 
04388          interface = strsep(&member, ";");
04389          penalty_tok = strsep(&member, ";");
04390          paused_tok = strsep(&member, ";");
04391          membername = strsep(&member, ";");
04392          state_interface = strsep(&member, ";");
04393 
04394          if (!penalty_tok) {
04395             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
04396             break;
04397          }
04398          penalty = strtol(penalty_tok, NULL, 10);
04399          if (errno == ERANGE) {
04400             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
04401             break;
04402          }
04403          
04404          if (!paused_tok) {
04405             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
04406             break;
04407          }
04408          paused = strtol(paused_tok, NULL, 10);
04409          if ((errno == ERANGE) || paused < 0 || paused > 1) {
04410             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
04411             break;
04412          }
04413 
04414          ast_debug(1, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
04415          
04416          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
04417             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
04418             break;
04419          }
04420       }
04421       queue_unref(cur_queue);
04422    }
04423 
04424    ao2_unlock(queues);
04425    if (db_tree) {
04426       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
04427       ast_db_freetree(db_tree);
04428    }
04429 }

static int reload_queue_rules ( int  reload  )  [static]

Definition at line 5365 of file app_queue.c.

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

Referenced by handle_queue_rule_reload(), and reload_queues().

05366 {
05367    struct ast_config *cfg;
05368    struct rule_list *rl_iter, *new_rl;
05369    struct penalty_rule *pr_iter;
05370    char *rulecat = NULL;
05371    struct ast_variable *rulevar = NULL;
05372    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05373    
05374    if (!(cfg = ast_config_load("queuerules.conf", config_flags))) {
05375       ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n");
05376    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
05377       ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n");
05378       return AST_MODULE_LOAD_SUCCESS;
05379    } else {
05380       AST_LIST_LOCK(&rule_lists);
05381       while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) {
05382          while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list)))
05383             ast_free(pr_iter);
05384          ast_free(rl_iter);
05385       }
05386       while ((rulecat = ast_category_browse(cfg, rulecat))) {
05387          if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) {
05388             ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n");
05389             AST_LIST_UNLOCK(&rule_lists);
05390             return AST_MODULE_LOAD_FAILURE;
05391          } else {
05392             ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name));
05393             AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list);
05394             for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next)
05395                if(!strcasecmp(rulevar->name, "penaltychange")) {
05396                   insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno);
05397                } else {
05398                   ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno);
05399                }
05400          }
05401       }
05402       AST_LIST_UNLOCK(&rule_lists);
05403    }
05404 
05405    ast_config_destroy(cfg);
05406 
05407    return AST_MODULE_LOAD_SUCCESS;
05408 }

static int reload_queues ( int  reload  )  [static]

Definition at line 5411 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_load, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_MODULE_LOAD_FAILURE, AST_STANDARD_APP_ARGS, 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, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, reload_queue_rules(), remove_from_interfaces(), member::state_interface, strat2int(), and var.

Referenced by load_module(), and reload().

05412 {
05413    struct call_queue *q;
05414    struct ast_config *cfg;
05415    char *cat, *tmp;
05416    struct ast_variable *var;
05417    struct member *cur, *newm;
05418    struct ao2_iterator mem_iter;
05419    int new;
05420    const char *general_val = NULL;
05421    char parse[80];
05422    char *interface, *state_interface;
05423    char *membername = NULL;
05424    int penalty;
05425    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
05426    struct ao2_iterator queue_iter;
05427    AST_DECLARE_APP_ARGS(args,
05428       AST_APP_ARG(interface);
05429       AST_APP_ARG(penalty);
05430       AST_APP_ARG(membername);
05431       AST_APP_ARG(state_interface);
05432    );
05433 
05434    /*First things first. Let's load queuerules.conf*/
05435    if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE)
05436       return AST_MODULE_LOAD_FAILURE;
05437       
05438    if (!(cfg = ast_config_load("queues.conf", config_flags))) {
05439       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
05440       return 0;
05441    } else if (cfg == CONFIG_STATUS_FILEUNCHANGED)
05442       return 0;
05443    ao2_lock(queues);
05444    use_weight=0;
05445    /* Mark all queues as dead for the moment */
05446    queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK);
05447    while ((q = ao2_iterator_next(&queue_iter))) {
05448       if (!q->realtime) {
05449          q->dead = 1;
05450          q->found = 0;
05451       }
05452       queue_unref(q);
05453    }
05454 
05455    /* Chug through config file */
05456    cat = NULL;
05457    while ((cat = ast_category_browse(cfg, cat)) ) {
05458       if (!strcasecmp(cat, "general")) {  
05459          /* Initialize global settings */
05460          queue_keep_stats = 0;
05461          if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats")))
05462             queue_keep_stats = ast_true(general_val);
05463          queue_persistent_members = 0;
05464          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
05465             queue_persistent_members = ast_true(general_val);
05466          autofill_default = 0;
05467          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
05468             autofill_default = ast_true(general_val);
05469          montype_default = 0;
05470          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) {
05471             if (!strcasecmp(general_val, "mixmonitor"))
05472                montype_default = 1;
05473          }
05474          update_cdr = 0;
05475          if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr")))
05476             update_cdr = ast_true(general_val);
05477          shared_lastcall = 0;
05478          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
05479             shared_lastcall = ast_true(general_val);
05480       } else { /* Define queue */
05481          /* Look for an existing one */
05482          struct call_queue tmpq = {
05483             .name = cat,
05484          };
05485          if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
05486             /* Make one then */
05487             if (!(q = alloc_queue(cat))) {
05488                /* TODO: Handle memory allocation failure */
05489             }
05490             new = 1;
05491          } else
05492             new = 0;
05493          if (q) {
05494             const char *tmpvar = NULL;
05495             if (!new)
05496                ao2_lock(q);
05497             /* Check if a queue with this name already exists */
05498             if (q->found) {
05499                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
05500                if (!new) {
05501                   ao2_unlock(q);
05502                   queue_unref(q);
05503                }
05504                continue;
05505             }
05506             /* Due to the fact that the "linear" strategy will have a different allocation
05507              * scheme for queue members, we must devise the queue's strategy before other initializations
05508              */
05509             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
05510                q->strategy = strat2int(tmpvar);
05511                if (q->strategy < 0) {
05512                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
05513                   tmpvar, q->name);
05514                   q->strategy = QUEUE_STRATEGY_RINGALL;
05515                }
05516             } else
05517                q->strategy = QUEUE_STRATEGY_RINGALL;
05518             /* Re-initialize the queue, and clear statistics */
05519             init_queue(q);
05520             if (!queue_keep_stats) 
05521                clear_queue(q);
05522             mem_iter = ao2_iterator_init(q->members, 0);
05523             while ((cur = ao2_iterator_next(&mem_iter))) {
05524                if (!cur->dynamic) {
05525                   cur->delme = 1;
05526                }
05527                ao2_ref(cur, -1);
05528             }
05529             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05530                if (!strcasecmp(var->name, "member")) {
05531                   struct member tmpmem;
05532                   membername = NULL;
05533 
05534                   if (ast_strlen_zero(var->value)) {
05535                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05536                      continue;
05537                   }
05538 
05539                   /* Add a new member */
05540                   ast_copy_string(parse, var->value, sizeof(parse));
05541                   
05542                   AST_STANDARD_APP_ARGS(args, parse);
05543 
05544                   interface = args.interface;
05545                   if (!ast_strlen_zero(args.penalty)) {
05546                      tmp = args.penalty;
05547                      while (*tmp && *tmp < 33) tmp++;
05548                      penalty = atoi(tmp);
05549                      if (penalty < 0) {
05550                         penalty = 0;
05551                      }
05552                   } else
05553                      penalty = 0;
05554 
05555                   if (!ast_strlen_zero(args.membername)) {
05556                      membername = args.membername;
05557                      while (*membername && *membername < 33) membername++;
05558                   }
05559 
05560                   if (!ast_strlen_zero(args.state_interface)) {
05561                      state_interface = args.state_interface;
05562                      while (*state_interface && *state_interface < 33) state_interface++;
05563                   } else
05564                      state_interface = interface;
05565 
05566                   /* Find the old position in the list */
05567                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05568                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05569                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
05570                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
05571                      remove_from_interfaces(cur->state_interface, 0);
05572                   }
05573                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05574                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
05575                      add_to_interfaces(state_interface);
05576                   ao2_link(q->members, newm);
05577                   ao2_ref(newm, -1);
05578                   newm = NULL;
05579 
05580                   if (cur)
05581                      ao2_ref(cur, -1);
05582                   else {
05583                      q->membercount++;
05584                   }
05585                } else {
05586                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05587                }
05588             }
05589 
05590             /* Free remaining members marked as delme */
05591             mem_iter = ao2_iterator_init(q->members, 0);
05592             while ((cur = ao2_iterator_next(&mem_iter))) {
05593                if (! cur->delme) {
05594                   ao2_ref(cur, -1);
05595                   continue;
05596                }
05597                q->membercount--;
05598                ao2_unlink(q->members, cur);
05599                remove_from_interfaces(cur->interface, 0);
05600                ao2_ref(cur, -1);
05601             }
05602 
05603             if (new) {
05604                ao2_link(queues, q);
05605             } else 
05606                ao2_unlock(q);
05607             queue_unref(q);
05608          }
05609       }
05610    }
05611    ast_config_destroy(cfg);
05612    queue_iter = ao2_iterator_init(queues, 0);
05613    while ((q = ao2_iterator_next(&queue_iter))) {
05614       if (q->dead) {
05615          ao2_unlink(queues, q);
05616       } else {
05617          ao2_lock(q);
05618          mem_iter = ao2_iterator_init(q->members, 0);
05619          while ((cur = ao2_iterator_next(&mem_iter))) {
05620             if (cur->dynamic)
05621                q->membercount++;
05622             cur->status = ast_device_state(cur->state_interface);
05623             ao2_ref(cur, -1);
05624          }
05625          ao2_unlock(q);
05626       }
05627       queue_unref(q);
05628    }
05629    ao2_unlock(queues);
05630    return 1;
05631 }

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

Definition at line 1046 of file app_queue.c.

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

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

01047 {
01048    struct member_interface *curint;
01049 
01050    if (interface_exists_global(interface, lock_queue_container))
01051       return 0;
01052 
01053    AST_LIST_LOCK(&interfaces);
01054    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01055       if (!strcasecmp(curint->interface, interface)) {
01056          ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01057          AST_LIST_REMOVE_CURRENT(list);
01058          ast_free(curint);
01059          break;
01060       }
01061    }
01062    AST_LIST_TRAVERSE_SAFE_END;
01063    AST_LIST_UNLOCK(&interfaces);
01064 
01065    return 0;
01066 }

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 4078 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 attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().

04079 {
04080    struct call_queue *q, tmpq = {
04081       .name = queuename,   
04082    };
04083    struct member *mem, tmpmem;
04084    int res = RES_NOSUCHQUEUE;
04085 
04086    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04087    if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) {
04088       ao2_lock(queues);
04089       ao2_lock(q);
04090       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
04091          /* XXX future changes should beware of this assumption!! */
04092          if (!mem->dynamic) {
04093             ao2_ref(mem, -1);
04094             ao2_unlock(q);
04095             queue_unref(q);
04096             ao2_unlock(queues);
04097             return RES_NOT_DYNAMIC;
04098          }
04099          q->membercount--;
04100          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
04101             "Queue: %s\r\n"
04102             "Location: %s\r\n"
04103             "MemberName: %s\r\n",
04104             q->name, mem->interface, mem->membername);
04105          ao2_unlink(q->members, mem);
04106          remove_from_interfaces(mem->state_interface, 0);
04107          ao2_ref(mem, -1);
04108 
04109          if (queue_persistent_members)
04110             dump_queue_members(q);
04111          
04112          res = RES_OKAY;
04113       } else {
04114          res = RES_EXISTS;
04115       }
04116       ao2_unlock(q);
04117       ao2_unlock(queues);
04118       queue_unref(q);
04119    }
04120 
04121    return res;
04122 }

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

Part 2 of ring_one.

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

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

Definition at line 2198 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_state(), AST_DEVICE_UNKNOWN, ast_free, ast_request(), ast_strdup, 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, ast_channel::dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, manager_event, callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, status, callattempt::stillgoing, ast_channel::uniqueid, update_status(), ast_cdr::userfield, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

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

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

References ast_debug, callattempt::chan, 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().

02379 {
02380    int ret = 0;
02381 
02382    while (ret == 0) {
02383       struct callattempt *best = find_best(outgoing);
02384       if (!best) {
02385          ast_debug(1, "Nobody left to try ringing in queue\n");
02386          break;
02387       }
02388       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02389          struct callattempt *cur;
02390          /* Ring everyone who shares this best metric (for ringall) */
02391          for (cur = outgoing; cur; cur = cur->q_next) {
02392             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02393                ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02394                ret |= ring_entry(qe, cur, busies);
02395             }
02396          }
02397       } else {
02398          /* Ring just the best channel */
02399          ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric);
02400          ret = ring_entry(qe, best, busies);
02401       }
02402    }
02403 
02404    return ret;
02405 }

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

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

02525 {
02526    ast_verb(3, "Nobody picked up in %d ms\n", rnatime);
02527    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02528    if (qe->parent->autopause && pause) {
02529       if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) {
02530          ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02531       } else {
02532          ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02533       }
02534    }
02535    return;
02536 }

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

RemoveQueueMember application.

Definition at line 4504 of file app_queue.c.

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

Referenced by load_module().

04505 {
04506    int res=-1;
04507    char *parse, *temppos = NULL;
04508    AST_DECLARE_APP_ARGS(args,
04509       AST_APP_ARG(queuename);
04510       AST_APP_ARG(interface);
04511       AST_APP_ARG(options);
04512    );
04513 
04514 
04515    if (ast_strlen_zero(data)) {
04516       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n");
04517       return -1;
04518    }
04519 
04520    parse = ast_strdupa(data);
04521 
04522    AST_STANDARD_APP_ARGS(args, parse);
04523 
04524    if (ast_strlen_zero(args.interface)) {
04525       args.interface = ast_strdupa(chan->name);
04526       temppos = strrchr(args.interface, '-');
04527       if (temppos)
04528          *temppos = '\0';
04529    }
04530 
04531    switch (remove_from_queue(args.queuename, args.interface)) {
04532    case RES_OKAY:
04533       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04534       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04535       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04536       res = 0;
04537       break;
04538    case RES_EXISTS:
04539       ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04540       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04541       res = 0;
04542       break;
04543    case RES_NOSUCHQUEUE:
04544       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04545       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04546       res = 0;
04547       break;
04548    case RES_NOT_DYNAMIC:
04549       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04550       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04551       res = 0;
04552       break;
04553    }
04554 
04555    return res;
04556 }

static void rt_handle_member_record ( struct call_queue q,
char *  interface,
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 1356 of file app_queue.c.

References add_to_interfaces(), ao2_find(), ao2_link(), ao2_ref(), ast_copy_string(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, OBJ_POINTER, member::paused, member::penalty, member::realtime, remove_from_interfaces(), and member::state_interface.

Referenced by update_realtime_members().

01357 {
01358    struct member *m, tmpmem;
01359    int penalty = 0;
01360    int paused  = 0;
01361 
01362    if (penalty_str) {
01363       penalty = atoi(penalty_str);
01364       if (penalty < 0)
01365          penalty = 0;
01366    }
01367 
01368    if (paused_str) {
01369       paused = atoi(paused_str);
01370       if (paused < 0)
01371          paused = 0;
01372    }
01373 
01374    /* Find the member, or the place to put a new one. */
01375    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01376    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01377 
01378    /* Create a new one if not found, else update penalty */
01379    if (!m) {
01380       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01381          m->dead = 0;
01382          m->realtime = 1;
01383          add_to_interfaces(m->state_interface);
01384          ao2_link(q->members, m);
01385          ao2_ref(m, -1);
01386          m = NULL;
01387          q->membercount++;
01388       }
01389    } else {
01390       m->dead = 0;   /* Do not delete this one. */
01391       if (paused_str)
01392          m->paused = paused;
01393       if (strcasecmp(state_interface, m->state_interface)) {
01394          remove_from_interfaces(m->state_interface, 0);
01395          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01396          add_to_interfaces(m->state_interface);
01397       }
01398       m->penalty = penalty;
01399       ao2_ref(m, -1);
01400    }
01401 }

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

Playback announcement to queued members if peroid has elapsed.

Definition at line 2456 of file app_queue.c.

References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_strlen_zero(), ast_verb, queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, MAX_PERIODIC_ANNOUNCEMENTS, queue_ent::moh, queue_ent::parent, call_queue::periodicannouncefrequency, play_file(), call_queue::sound_periodicannounce, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

02457 {
02458    int res = 0;
02459    time_t now;
02460 
02461    /* Get the current time */
02462    time(&now);
02463 
02464    /* Check to see if it is time to announce */
02465    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02466       return 0;
02467 
02468    /* Stop the music on hold so we can play our own file */
02469    if (ringing)
02470       ast_indicate(qe->chan,-1);
02471    else
02472       ast_moh_stop(qe->chan);
02473 
02474    ast_verb(3, "Playing periodic announcement\n");
02475 
02476    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02477    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || 
02478       !qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound] ||
02479       ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) {
02480       qe->last_periodic_announce_sound = 0;
02481    }
02482    
02483    /* play the announcement */
02484    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str);
02485 
02486    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02487       res = 0;
02488 
02489    /* Resume Music on Hold if the caller is going to stay in the queue */
02490    if (!res) {
02491       if (ringing)
02492          ast_indicate(qe->chan, AST_CONTROL_RINGING);
02493       else
02494          ast_moh_start(qe->chan, qe->moh, NULL);
02495    }
02496 
02497    /* update last_periodic_announce_time */
02498    qe->last_periodic_announce_time = now;
02499 
02500    /* Update the current periodic announcement to the next announcement */
02501    qe->last_periodic_announce_sound++;
02502    
02503    return res;
02504 }

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

Definition at line 1844 of file app_queue.c.

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

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

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

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

03096 {
03097    const char *reason = NULL; /* silence dumb compilers */
03098 
03099    if (!qe->parent->eventwhencalled)
03100       return;
03101 
03102    switch (rsn) {
03103    case CALLER:
03104       reason = "caller";
03105       break;
03106    case AGENT:
03107       reason = "agent";
03108       break;
03109    case TRANSFER:
03110       reason = "transfer";
03111       break;
03112    }
03113 
03114    manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03115       "Queue: %s\r\n"
03116       "Uniqueid: %s\r\n"
03117       "Channel: %s\r\n"
03118       "Member: %s\r\n"
03119       "MemberName: %s\r\n"
03120       "HoldTime: %ld\r\n"
03121       "TalkTime: %ld\r\n"
03122       "Reason: %s\r\n"
03123       "%s",
03124       queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03125       (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
03126       qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
03127 }

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

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

04187 {
04188    int found = 0;
04189    struct call_queue *q;
04190    struct member *mem;
04191    struct ao2_iterator queue_iter;
04192    int failed;
04193 
04194    /* Special event for when all queues are paused - individual events still generated */
04195    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
04196    if (ast_strlen_zero(queuename))
04197       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
04198 
04199    queue_iter = ao2_iterator_init(queues, 0);
04200    while ((q = ao2_iterator_next(&queue_iter))) {
04201       ao2_lock(q);
04202       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04203          if ((mem = interface_exists(q, interface))) {
04204             found++;
04205             if (mem->paused == paused) {
04206                ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
04207             }
04208             
04209             failed = 0;
04210             if (mem->realtime) {
04211                failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
04212             }
04213 
04214             if (failed) {
04215                ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface);
04216                ao2_ref(mem, -1);
04217                ao2_unlock(q);
04218                continue;
04219             }
04220 
04221             mem->paused = paused;
04222 
04223             if (queue_persistent_members)
04224                dump_queue_members(q);
04225 
04226             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, ""));
04227             
04228             if (!ast_strlen_zero(reason)) {
04229                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04230                   "Queue: %s\r\n"
04231                   "Location: %s\r\n"
04232                   "MemberName: %s\r\n"
04233                   "Paused: %d\r\n"
04234                   "Reason: %s\r\n",
04235                      q->name, mem->interface, mem->membername, paused, reason);
04236             } else {
04237                manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
04238                   "Queue: %s\r\n"
04239                   "Location: %s\r\n"
04240                   "MemberName: %s\r\n"
04241                   "Paused: %d\r\n",
04242                      q->name, mem->interface, mem->membername, paused);
04243             }
04244             ao2_ref(mem, -1);
04245          }
04246       }
04247       ao2_unlock(q);
04248       queue_unref(q);
04249    }
04250 
04251    return found ? RESULT_SUCCESS : RESULT_FAILURE;
04252 }

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

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

04256 {
04257    int foundinterface = 0, foundqueue = 0;
04258    struct call_queue *q;
04259    struct member *mem;
04260    struct ao2_iterator queue_iter;
04261 
04262    if (penalty < 0) {
04263       ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty);
04264       return RESULT_FAILURE;
04265    }
04266 
04267    queue_iter = ao2_iterator_init(queues, 0);
04268    while ((q = ao2_iterator_next(&queue_iter))) {
04269       ao2_lock(q);
04270       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
04271          foundqueue++;
04272          if ((mem = interface_exists(q, interface))) {
04273             foundinterface++;
04274             mem->penalty = penalty;
04275             
04276             ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty);
04277             manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty",
04278                "Queue: %s\r\n"
04279                "Location: %s\r\n"
04280                "Penalty: %d\r\n",
04281                q->name, mem->interface, penalty);
04282             ao2_ref(mem, -1);
04283          }
04284       }
04285       ao2_unlock(q);
04286       queue_unref(q);
04287    }
04288 
04289    if (foundinterface) {
04290       return RESULT_SUCCESS;
04291    } else if (!foundqueue) {
04292       ast_log (LOG_ERROR, "Invalid queuename\n"); 
04293    } else {
04294       ast_log (LOG_ERROR, "Invalid interface\n");
04295    }  
04296 
04297    return RESULT_FAILURE;
04298 }

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

sets the QUEUESTATUS channel variable

Definition at line 533 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00534 {
00535    int i;
00536 
00537    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00538       if (queue_results[i].id == res) {
00539          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00540          return;
00541       }
00542    }
00543 }

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

Set variables of queue.

Definition at line 595 of file app_queue.c.

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

Referenced by end_bridge_callback(), and record_abandoned().

00596 {
00597 
00598    char interfacevar[256]="";
00599    float sl = 0;
00600         
00601    if (q->setqueuevar) {
00602       sl = 0;
00603       if (q->callscompleted > 0) 
00604          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
00605 
00606       snprintf(interfacevar, sizeof(interfacevar),
00607          "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f",
00608          q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned,  q->servicelevel, sl);
00609    
00610       pbx_builtin_setvar_multiple(chan, interfacevar); 
00611    }
00612 }

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

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

Definition at line 3197 of file app_queue.c.

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

03198 {
03199    struct ast_datastore *ds;
03200    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
03201 
03202    if (!qtds) {
03203       ast_log(LOG_WARNING, "Memory allocation error!\n");
03204       return NULL;
03205    }
03206 
03207    ast_channel_lock(qe->chan);
03208    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
03209       ast_channel_unlock(qe->chan);
03210       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
03211       return NULL;
03212    }
03213 
03214    qtds->qe = qe;
03215    /* This member is refcounted in try_calling, so no need to add it here, too */
03216    qtds->member = member;
03217    qtds->starttime = starttime;
03218    qtds->callcompletedinsl = callcompletedinsl;
03219    ds->data = qtds;
03220    ast_channel_datastore_add(qe->chan, ds);
03221    ast_channel_unlock(qe->chan);
03222    return ds;
03223 }

static int statechange_queue ( const char *  dev,
enum ast_device_state  state 
) [static]

Producer of the statechange queue.

Definition at line 829 of file app_queue.c.

References ast_calloc, ast_cond_signal(), AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), device_state, and statechange::entry.

00830 {
00831    struct statechange *sc;
00832 
00833    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00834       return 0;
00835 
00836    sc->state = state;
00837    strcpy(sc->dev, dev);
00838 
00839    ast_mutex_lock(&device_state.lock);
00840    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00841    ast_cond_signal(&device_state.cond);
00842    ast_mutex_unlock(&device_state.lock);
00843 
00844    return 0;
00845 }

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

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

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

02409 {
02410    struct callattempt *best = find_best(outgoing);
02411 
02412    if (best) {
02413       /* Ring just the best channel */
02414       ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric);
02415       qe->parent->rrpos = best->metric % 1000;
02416    } else {
02417       /* Just increment rrpos */
02418       if (qe->parent->wrapped) {
02419          /* No more channels, start over */
02420          qe->parent->rrpos = 0;
02421       } else {
02422          /* Prioritize next entry */
02423          qe->parent->rrpos++;
02424       }
02425    }
02426    qe->parent->wrapped = 0;
02427 
02428    return 0;
02429 }

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

Definition at line 557 of file app_queue.c.

References strategies.

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

00558 {
00559    int x;
00560 
00561    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00562       if (!strcasecmp(strategy, strategies[x].name))
00563          return strategies[x].strategy;
00564    }
00565 
00566    return -1;
00567 }

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

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

Referenced by queue_exec().

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

static int unload_module ( void   )  [static]

Definition at line 6587 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), ast_cli_unregister_multiple(), ast_cond_signal(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_event_unsubscribe(), ast_manager_unregister(), ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state, device_state_sub, queue_unref(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, and queuewaitingcount_function.

06588 {
06589    int res;
06590    struct ast_context *con;
06591    struct ao2_iterator q_iter;
06592    struct call_queue *q = NULL;
06593 
06594 
06595    if (device_state.thread != AST_PTHREADT_NULL) {
06596       device_state.stop = 1;
06597       ast_mutex_lock(&device_state.lock);
06598       ast_cond_signal(&device_state.cond);
06599       ast_mutex_unlock(&device_state.lock);
06600       pthread_join(device_state.thread, NULL);
06601    }
06602 
06603    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
06604    res = ast_manager_unregister("QueueStatus");
06605    res |= ast_manager_unregister("Queues");
06606    res |= ast_manager_unregister("QueueRule");
06607    res |= ast_manager_unregister("QueueSummary");
06608    res |= ast_manager_unregister("QueueAdd");
06609    res |= ast_manager_unregister("QueueRemove");
06610    res |= ast_manager_unregister("QueuePause");
06611    res |= ast_manager_unregister("QueueLog");
06612    res |= ast_manager_unregister("QueuePenalty");
06613    res |= ast_unregister_application(app_aqm);
06614    res |= ast_unregister_application(app_rqm);
06615    res |= ast_unregister_application(app_pqm);
06616    res |= ast_unregister_application(app_upqm);
06617    res |= ast_unregister_application(app_ql);
06618    res |= ast_unregister_application(app);
06619    res |= ast_custom_function_unregister(&queuevar_function);
06620    res |= ast_custom_function_unregister(&queuemembercount_function);
06621    res |= ast_custom_function_unregister(&queuemembercount_dep);
06622    res |= ast_custom_function_unregister(&queuememberlist_function);
06623    res |= ast_custom_function_unregister(&queuewaitingcount_function);
06624    res |= ast_custom_function_unregister(&queuememberpenalty_function);
06625 
06626    if (device_state_sub)
06627       ast_event_unsubscribe(device_state_sub);
06628 
06629    if ((con = ast_context_find("app_queue_gosub_virtual_context"))) {
06630       ast_context_remove_extension2(con, "s", 1, NULL, 0);
06631       ast_context_destroy(con, "app_queue"); /* leave no trace */
06632    }
06633 
06634    clear_and_free_interfaces();
06635 
06636    q_iter = ao2_iterator_init(queues, 0);
06637    while ((q = ao2_iterator_next(&q_iter))) {
06638       ao2_unlink(queues, q);
06639       queue_unref(q);
06640    }
06641    ao2_ref(queues, -1);
06642 
06643    return res;
06644 }

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

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

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

02847 {
02848    int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value;
02849    int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value;
02850    char max_penalty_str[20], min_penalty_str[20]; 
02851    /* a relative change to the penalty could put it below 0 */
02852    if (max_penalty < 0)
02853       max_penalty = 0;
02854    if (min_penalty < 0)
02855       min_penalty = 0;
02856    if (min_penalty > max_penalty)
02857       min_penalty = max_penalty;
02858    snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty);
02859    snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty);
02860    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str);
02861    pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str);
02862    qe->max_penalty = max_penalty;
02863    qe->min_penalty = min_penalty;
02864    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);
02865    qe->pr = AST_LIST_NEXT(qe->pr, list);
02866 }

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 2987 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, OBJ_POINTER, and queues.

Referenced by queue_transfer_fixup().

02988 {
02989    struct member *mem;
02990    struct call_queue *qtmp;
02991    struct ao2_iterator queue_iter;  
02992    
02993    if (shared_lastcall) {
02994       queue_iter = ao2_iterator_init(queues, 0);
02995       while ((qtmp = ao2_iterator_next(&queue_iter))) {
02996          ao2_lock(qtmp);
02997          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
02998             time(&mem->lastcall);
02999             mem->calls++;
03000             mem->lastqueue = q;
03001             ao2_ref(mem, -1);
03002          }
03003          ao2_unlock(qtmp);
03004          ao2_ref(qtmp, -1);
03005       }
03006    } else {
03007       ao2_lock(q);
03008       time(&member->lastcall);
03009       member->calls++;
03010       member->lastqueue = q;
03011       ao2_unlock(q);
03012    }  
03013    ao2_lock(q);
03014    q->callscompleted++;
03015    if (callcompletedinsl)
03016       q->callscompletedinsl++;
03017    ao2_unlock(q);
03018    return 0;
03019 }

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

Definition at line 1635 of file app_queue.c.

References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), ast_variables_destroy(), member::interface, and var.

Referenced by set_member_paused().

01636 {
01637    struct ast_variable *var, *origvar;
01638    int ret = -1;
01639 
01640    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) {
01641       return ret;
01642    }
01643 
01644    origvar = var;
01645    while (var) {
01646       if (!strcmp(var->name, "uniqueid")) {
01647          break;
01648       }
01649       var = var->next;
01650    }
01651    if (var && !ast_strlen_zero(var->value)) {
01652       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1) {
01653          ret = 0;
01654       }
01655    }
01656    ast_variables_destroy(origvar);
01657    return ret;
01658 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1660 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_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, and member::state_interface.

Referenced by load_realtime_queue(), and queue_exec().

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

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 694 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, member::membername, member::paused, member::penalty, queue_unref(), queues, member::realtime, member::state_interface, and member::status.

Referenced by handle_statechange(), and ring_entry().

00695 {
00696    struct member *cur;
00697    struct ao2_iterator mem_iter, queue_iter;
00698    struct call_queue *q;
00699    char tmp_interface[80];
00700 
00701    queue_iter = ao2_iterator_init(queues, 0);
00702    while ((q = ao2_iterator_next(&queue_iter))) {
00703       ao2_lock(q);
00704       mem_iter = ao2_iterator_init(q->members, 0);
00705       while ((cur = ao2_iterator_next(&mem_iter))) {
00706          char *slash_pos;
00707          ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
00708          if ((slash_pos = strchr(tmp_interface, '/')))
00709             if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/')))
00710                *slash_pos = '\0';
00711 
00712          if (strcasecmp(interface, tmp_interface)) {
00713             ao2_ref(cur, -1);
00714             continue;
00715          }
00716 
00717          if (cur->status != status) {
00718             cur->status = status;
00719             if (q->maskmemberstatus) {
00720                ao2_ref(cur, -1);
00721                continue;
00722             }
00723 
00724             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00725                "Queue: %s\r\n"
00726                "Location: %s\r\n"
00727                "MemberName: %s\r\n"
00728                "Membership: %s\r\n"
00729                "Penalty: %d\r\n"
00730                "CallsTaken: %d\r\n"
00731                "LastCall: %d\r\n"
00732                "Status: %d\r\n"
00733                "Paused: %d\r\n",
00734                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00735                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00736          }
00737          ao2_ref(cur, -1);
00738       }
00739       queue_unref(q);
00740       ao2_unlock(q);
00741    }
00742 
00743    return 0;
00744 }

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

UnPauseQueueMember application.

Definition at line 4468 of file app_queue.c.

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

Referenced by load_module().

04469 {
04470    char *parse;
04471    AST_DECLARE_APP_ARGS(args,
04472       AST_APP_ARG(queuename);
04473       AST_APP_ARG(interface);
04474       AST_APP_ARG(options);
04475       AST_APP_ARG(reason);
04476    );
04477 
04478    if (ast_strlen_zero(data)) {
04479       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n");
04480       return -1;
04481    }
04482 
04483    parse = ast_strdupa(data);
04484 
04485    AST_STANDARD_APP_ARGS(args, parse);
04486 
04487    if (ast_strlen_zero(args.interface)) {
04488       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n");
04489       return -1;
04490    }
04491 
04492    if (set_member_paused(args.queuename, args.interface, args.reason, 0)) {
04493       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04494       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04495       return 0;
04496    }
04497 
04498    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04499 
04500    return 0;
04501 }

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

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

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

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

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

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

Referenced by ring_entry(), and send_agent_complete().

02148 {
02149    struct ast_str *buf = ast_str_alloca(len + 1);
02150    char *tmp;
02151 
02152    if (pbx_builtin_serialize_variables(chan, &buf)) {
02153       int i, j;
02154 
02155       /* convert "\n" to "\nVariable: " */
02156       strcpy(vars, "Variable: ");
02157       tmp = buf->str;
02158 
02159       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
02160          vars[j] = tmp[i];
02161 
02162          if (tmp[i + 1] == '\0')
02163             break;
02164          if (tmp[i] == '\n') {
02165             vars[j++] = '\r';
02166             vars[j++] = '\n';
02167 
02168             ast_copy_string(&(vars[j]), "Variable: ", len - j);
02169             j += 9;
02170          }
02171       }
02172       if (j > len - 3)
02173          j = len - 3;
02174       vars[j++] = '\r';
02175       vars[j++] = '\n';
02176       vars[j] = '\0';
02177    } else {
02178       /* there are no channel variables; leave it blank */
02179       *vars = '\0';
02180    }
02181    return vars;
02182 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3997 of file app_queue.c.

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

Referenced by queue_exec().

03998 {
03999    /* Don't need to hold the lock while we setup the outgoing calls */
04000    int retrywait = qe->parent->retry * 1000;
04001 
04002    int res = ast_waitfordigit(qe->chan, retrywait);
04003    if (res > 0 && !valid_exit(qe, res))
04004       res = 0;
04005 
04006    return res;
04007 }

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

Wait for a member to answer the call.

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

Definition at line 2549 of file app_queue.c.

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

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

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

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

Referenced by queue_exec().

02879 {
02880    int res = 0;
02881 
02882    /* This is the holding pen for callers 2 through maxlen */
02883    for (;;) {
02884       enum queue_member_status stat = QUEUE_NORMAL;
02885       int exit = 0;
02886 
02887       if (is_our_turn(qe))
02888          break;
02889 
02890       /* If we have timed out, break out */
02891       if (qe->expire && (time(NULL) >= qe->expire)) {
02892          *reason = QUEUE_TIMEOUT;
02893          break;
02894       }
02895 
02896       /* If we are going to exit due to a leavewhenempty condition, we should
02897        * actually attempt to keep the caller in the queue until we have
02898        * exhausted all penalty rules.
02899        */
02900       for (; !exit || qe->pr; update_qe_rule(qe)) {
02901          stat = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty);
02902 
02903          if (!qe->pr || stat == QUEUE_NORMAL) {
02904             break;
02905          }
02906 
02907          /* leave the queue if no agents, if enabled */
02908          if ((qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) ||
02909                ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) ||
02910                ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS))) {
02911             continue;
02912          } else {
02913             exit = 1;
02914          }
02915       }
02916 
02917       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02918          *reason = QUEUE_LEAVEEMPTY;
02919          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02920          leave_queue(qe);
02921          break;
02922       }
02923 
02924       /* leave the queue if no reachable agents, if enabled */
02925       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) {
02926          *reason = QUEUE_LEAVEUNAVAIL;
02927          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02928          leave_queue(qe);
02929          break;
02930       }
02931       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02932          *reason = QUEUE_LEAVEUNAVAIL;
02933          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start);
02934          leave_queue(qe);
02935          break;
02936       }
02937 
02938       /* Make a position announcement, if enabled */
02939       if (qe->parent->announcefrequency &&
02940          (res = say_position(qe,ringing)))
02941          break;
02942 
02943       /* If we have timed out, break out */
02944       if (qe->expire && (time(NULL) >= qe->expire)) {
02945          *reason = QUEUE_TIMEOUT;
02946          break;
02947       }
02948 
02949       /* Make a periodic announcement, if enabled */
02950       if (qe->parent->periodicannouncefrequency &&
02951          (res = say_periodic_announcement(qe,ringing)))
02952          break;
02953       
02954       /* see if we need to move to the next penalty level for this queue */
02955       while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) {
02956          update_qe_rule(qe);
02957       }
02958 
02959       /* If we have timed out, break out */
02960       if (qe->expire && (time(NULL) >= qe->expire)) {
02961          *reason = QUEUE_TIMEOUT;
02962          break;
02963       }
02964       
02965       /* Wait a second before checking again */
02966       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02967          if (res > 0 && !valid_exit(qe, res))
02968             res = 0;
02969          else
02970             break;
02971       }
02972       
02973       /* If we have timed out, break out */
02974       if (qe->expire && (time(NULL) >= qe->expire)) {
02975          *reason = QUEUE_TIMEOUT;
02976          break;
02977       }
02978    }
02979 
02980    return res;
02981 }


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 = "068e67f60f50dd9ee86464c05884a49d" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 6707 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 150 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 209 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 211 of file app_queue.c.

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

Definition at line 210 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 235 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 237 of file app_queue.c.

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

Definition at line 236 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 269 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 222 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 224 of file app_queue.c.

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

Definition at line 223 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 253 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 255 of file app_queue.c.

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

Definition at line 254 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 6707 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 290 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 6577 of file app_queue.c.

Referenced by load_module(), and unload_module().

ast_cond_t cond

Condition for the state change queue

Definition at line 786 of file app_queue.c.

char* descrip [static]

Definition at line 154 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 299 of file app_queue.c.

struct statechange* first

Definition at line 788 of file app_queue.c.

enum queue_result id

Definition at line 316 of file app_queue.c.

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

struct statechange* last

Definition at line 788 of file app_queue.c.

ast_mutex_t lock

Lock for the state change queue

Definition at line 784 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 293 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 276 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 6568 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 6574 of file app_queue.c.

int queue_keep_stats = 0 [static]

queues.conf [general] option

Definition at line 281 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 284 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

struct ast_datastore_info queue_transfer_info [static]

Initial value:

 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}
a datastore used to help correctly log attended transfers of queue callers

Definition at line 3144 of file app_queue.c.

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

struct ast_custom_function queuemembercount_dep [static]

Definition at line 5327 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 5314 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 5346 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberpenalty_function [static]

Definition at line 5355 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 524 of file app_queue.c.

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

struct ast_custom_function queuevar_function [static]

Definition at line 5296 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

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

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 296 of file app_queue.c.

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

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

char* text

Definition at line 317 of file app_queue.c.

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

pthread_t thread

The device state monitoring thread

Definition at line 782 of file app_queue.c.

int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 302 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 287 of file app_queue.c.


Generated on Thu Jul 9 13:40:48 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7