Tue Mar 2 17:32:19 2010

Asterisk developer's documentation


app_queue.c File Reference

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

#include "asterisk.h"
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <ctype.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/event.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/global_datastores.h"
#include "asterisk/taskprocessor.h"
Include dependency graph for app_queue.c:

Go to the source code of this file.

Data Structures

struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  interfaces
struct  member
struct  member_interface
struct  penalty_rule
struct  queue_end_bridge
struct  queue_ent
struct  queue_transfer_ds
struct  rule_list
struct  rule_lists
struct  statechange
struct  strategy

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define ANNOUNCEPOSITION_LIMIT   4
#define ANNOUNCEPOSITION_MORE_THAN   3
#define ANNOUNCEPOSITION_NO   2
#define ANNOUNCEPOSITION_YES   1
#define AST_MAX_WATCHERS   256
#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define MAX_PERIODIC_ANNOUNCEMENTS   10
#define MAX_QUEUE_BUCKETS   53
#define PM_MAX_LEN   8192
#define QUEUE_EMPTY_LOOSE   3
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_EVENT_VARIABLES   3
#define queue_t_ref(a, b)   queue_ref(a)
#define queue_t_unref(a, b)   queue_unref(a)
#define queues_t_link(c, q, tag)   ao2_t_link(c,q,tag)
#define queues_t_unlink(c, q, tag)   ao2_t_unlink(c,q,tag)
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM,
  QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM
}
enum  agent_complete_reason { CALLER, AGENT, TRANSFER }
enum  queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7
}
enum  queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF }

Functions

static char * __queues_show (struct mansession *s, int fd, int argc, char **argv)
 Show queue(s) status and statistics.
static void __reg_module (void)
static void __unreg_module (void)
static int add_to_interfaces (const char *interface)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
 Add member to queue.
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, void *data)
 AddQueueMember application.
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
static void clear_and_free_interfaces (void)
static void clear_queue (struct call_queue *q)
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_pause_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_rule_show (const char *line, const char *word, int pos, int state)
static char * complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static void copy_rules (struct queue_ent *qe, const char *rulename)
 Copy rule from global list into specified queue.
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (void *obj)
 Free queue's member list then its string fields.
static void device_state_cb (const struct ast_event *event, void *unused)
static void do_hang (struct callattempt *o)
 common hangup actions
static void do_print (struct mansession *s, int fd, const char *str)
 direct ouput to manager or cli with proper terminator
static void dump_queue_members (struct call_queue *pm_queue)
 Dump all members in a specific queue to the database.
static void end_bridge_callback (void *data)
static void end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator)
static struct callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config)
 Reload a single queue via realtime.
static void free_members (struct call_queue *q, int all)
 Iterate through queue's member list and delete them.
static int get_member_penalty (char *queuename, char *interface)
static enum queue_member_status get_member_status (struct call_queue *q, int max_penalty, int min_penalty)
 Check if members are available.
static char * handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static char * handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int handle_statechange (void *datap)
 set a member's status based on device state of that member's interface
static void hangupcalls (struct callattempt *outgoing, struct ast_channel *exception)
 Hang up a list of outgoing calls.
static void init_queue (struct call_queue *q)
 Initialize Queue default values.
static void insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos)
 Insert the 'new' entry after the 'prev' entry of queue 'q'.
static int insert_penaltychange (const char *list_name, const char *content, const int linenum)
 Change queue penalty by adding rule.
static const char * int2strat (int strategy)
static struct memberinterface_exists (struct call_queue *q, const char *interface)
static int interface_exists_global (const char *interface, int lock_queue_container)
static int is_our_turn (struct queue_ent *qe)
 Check if we should start attempting to call queue members.
static int join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, const char *overriding_rule)
static void leave_queue (struct queue_ent *qe)
 Caller leaving queue.
static int load_module (void)
static struct call_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_queue_log_custom (struct mansession *s, const struct message *m)
static int manager_queue_member_penalty (struct mansession *s, const struct message *m)
static int manager_queue_rule_show (struct mansession *s, const struct message *m)
static int manager_queues_show (struct mansession *s, const struct message *m)
static int manager_queues_status (struct mansession *s, const struct message *m)
 Queue status info via AMI.
static int manager_queues_summary (struct mansession *s, const struct message *m)
 Summary of queue info via the AMI.
static int manager_remove_queue_member (struct mansession *s, const struct message *m)
static int member_cmp_fn (void *obj1, void *obj2, int flags)
static int member_hash_fn (const void *obj, const int flags)
static int num_available_members (struct call_queue *q)
 Get the number of members available to accept a call.
static int play_file (struct ast_channel *chan, const char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
 PauseQueueMember application.
static int ql_exec (struct ast_channel *chan, void *data)
 QueueLog application.
static int queue_cmp_cb (void *obj, void *arg, int flags)
static int queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
static int queue_function_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty.
static int queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value)
 Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty.
static int queue_function_qac (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get number either busy / free or total members of a specific queue.
static int queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Get the total number of members in a specific queue (Deprecated).
static int queue_function_queuememberlist (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue.
static int queue_function_queuewaitingcount (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue.
static int queue_function_var (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
 create interface var with all queue details.
static int queue_hash_cb (const void *obj, const int flags)
static struct call_queuequeue_ref (struct call_queue *q)
static void queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
 Configure a queue parameter.
static char * queue_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void queue_transfer_destroy (void *data)
static void queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 Log an attended transfer when a queue caller channel is masqueraded.
static struct call_queuequeue_unref (struct call_queue *q)
static void recalc_holdtime (struct queue_ent *qe, int newholdtime)
static void record_abandoned (struct queue_ent *qe)
 Record that a caller gave up on waiting in queue.
static int reload (void)
static void reload_queue_members (void)
 Reload dynamic queue members persisted into the astdb.
static int reload_queue_rules (int reload)
static int reload_queues (int reload)
static int remove_from_interfaces (const char *interface, int lock_queue_container)
static int remove_from_queue (const char *queuename, const char *interface)
 Remove member from queue.
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
 Part 2 of ring_one.
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member.
static void rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
 RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
static int rqm_exec (struct ast_channel *chan, void *data)
 RemoveQueueMember application.
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
 Find rt member record to update otherwise create one.
static int say_periodic_announcement (struct queue_ent *qe, int ringing)
 Playback announcement to queued members if peroid has elapsed.
static int say_position (struct queue_ent *qe, int ringing)
static void send_agent_complete (const struct queue_ent *qe, const char *queuename, const struct ast_channel *peer, const struct member *member, time_t callstart, char *vars, size_t vars_len, enum agent_complete_reason rsn)
 Send out AMI message with member call completion status information.
static int set_member_paused (const char *queuename, const char *interface, const char *reason, int paused)
static int set_member_penalty (char *queuename, char *interface, int penalty)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
 sets the QUEUESTATUS channel variable
static void set_queue_variables (struct call_queue *q, struct ast_channel *chan)
 Set variables of queue.
static struct ast_datastoresetup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl)
 create a datastore for storing relevant info to log attended transfers in the queue_log
static int store_next_lin (struct queue_ent *qe, struct callattempt *outgoing)
 Search for best metric and add to Linear queue.
static int store_next_rr (struct queue_ent *qe, struct callattempt *outgoing)
 Search for best metric and add to Round Robbin queue.
static int strat2int (const char *strategy)
static int try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
 A large function which calls members, updates statistics, and bridges the caller and a member.
static int unload_module (void)
static void update_qe_rule (struct queue_ent *qe)
 update rules for queues
static int update_queue (struct call_queue *q, struct member *member, int callcompletedinsl)
 update the queue status
static int update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value)
static void update_realtime_members (struct call_queue *q)
static int update_status (const char *interface, const int status)
 set a member's status based on device state of that member's state_interface.
static int upqm_exec (struct ast_channel *chan, void *data)
 UnPauseQueueMember application.
static int valid_exit (struct queue_ent *qe, char digit)
 Check for valid exit from queue via goto.
static char * vars2manager (struct ast_channel *chan, char *vars, size_t len)
 convert "\n" to "\nVariable: " ready for manager to use
static int wait_a_bit (struct queue_ent *qe)
static struct callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 Wait for a member to answer the call.
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)
 The waiting areas for callers who are not actively calling members.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static struct ast_module_infoast_module_info = &__mod_info
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_queue []
static char * descrip
static struct ast_event_subdevice_state_sub
 Subscription to device state change events.
static struct ast_taskprocessordevicestate_tps
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static const char qpm_cmd_usage []
static const char qsmp_cmd_usage []
static int queue_keep_stats = 0
 queues.conf [general] option
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queuemembercount_dep
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpenalty_function
static struct ao2_containerqueues
static struct ast_custom_function queuevar_function
static struct ast_custom_function queuewaitingcount_function
static const char qum_cmd_usage []
static int shared_lastcall = 0
 queues.conf [general] option
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int update_cdr = 0
 queues.conf [general] option
static int use_weight = 0
 queues.conf per-queue weight option

Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>

Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
These features added by David C. Troy <dave@toad.net>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

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

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

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 420 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 421 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_LIMIT   4

We not announce position more than <limit>

Definition at line 436 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_MORE_THAN   3

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

Definition at line 435 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define ANNOUNCEPOSITION_NO   2

We don't announce position

Definition at line 434 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEPOSITION_YES   1

We announce position

Definition at line 433 of file app_queue.c.

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

#define AST_MAX_WATCHERS   256

Definition at line 2615 of file app_queue.c.

#define DEFAULT_MIN_ANNOUNCE_FREQUENCY   15

The minimum number of seconds between position announcements \ The default value of 15 provides backwards compatibility

Definition at line 144 of file app_queue.c.

Referenced by init_queue().

#define DEFAULT_RETRY   5

Definition at line 140 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 141 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

The maximum periodic announcements we can have

Definition at line 143 of file app_queue.c.

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

#define MAX_QUEUE_BUCKETS   53

Definition at line 146 of file app_queue.c.

Referenced by load_module().

#define PM_MAX_LEN   8192

Definition at line 282 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_LOOSE   3

Definition at line 419 of file app_queue.c.

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

#define QUEUE_EMPTY_NORMAL   1

Definition at line 417 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 418 of file app_queue.c.

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

#define QUEUE_EVENT_VARIABLES   3

Definition at line 422 of file app_queue.c.

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

#define queue_t_ref ( a,
 )     queue_ref(a)

Definition at line 613 of file app_queue.c.

Referenced by leave_queue(), and try_calling().

#define queue_t_unref ( a,
 )     queue_unref(a)
#define queues_t_link ( c,
q,
tag   )     ao2_t_link(c,q,tag)

Definition at line 615 of file app_queue.c.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

Definition at line 616 of file app_queue.c.

Referenced by find_queue_by_name_rt(), leave_queue(), reload_queues(), and unload_module().

#define RECHECK   1

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

Definition at line 142 of file app_queue.c.

Referenced by wait_our_turn().

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

Member is not dynamic

Definition at line 152 of file app_queue.c.

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

#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumeration Type Documentation

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

Definition at line 114 of file app_queue.c.

Enumerator:
CALLER 
AGENT 
TRANSFER 

Definition at line 3183 of file app_queue.c.

03183                            {
03184    CALLER,
03185    AGENT,
03186    TRANSFER
03187 };

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 674 of file app_queue.c.

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 308 of file app_queue.c.

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

Enumerator:
TIMEOUT_PRIORITY_APP 
TIMEOUT_PRIORITY_CONF 

Definition at line 333 of file app_queue.c.

00333                             {
00334    TIMEOUT_PRIORITY_APP,
00335    TIMEOUT_PRIORITY_CONF,
00336 };


Function Documentation

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

Show queue(s) status and statistics.

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

Definition at line 5795 of file app_queue.c.

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

Referenced by manager_queues_show(), and queue_show().

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

static void __reg_module ( void   )  [static]

Definition at line 6865 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 6865 of file app_queue.c.

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

Definition at line 983 of file app_queue.c.

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

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

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

static int add_to_queue ( const char *  queuename,
const char *  interface,
const char *  membername,
int  penalty,
int  paused,
int  dump,
const char *  state_interface 
) [static]

Add member to queue.

Return values:
RES_NOT_DYNAMIC when they aren't a RT member
RES_NOSUCHQUEUE queue does not exist
RES_OKAY added member from queue
RES_EXISTS queue exists but no members
RES_OUT_OF_MEMORY queue exists but not enough memory to create member

Note:
Ensure the appropriate realtime queue is loaded. Note that this short-circuits if the queue is already in memory.

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

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

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

Definition at line 1466 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

AddQueueMember application.

Definition at line 4689 of file app_queue.c.

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

Referenced by load_module().

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

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

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

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

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

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

Referenced by try_calling().

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

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1059 of file app_queue.c.

References ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, and AST_LIST_UNLOCK.

Referenced by unload_module().

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

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 974 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

Definition at line 2158 of file app_queue.c.

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

Referenced by ring_entry().

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

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

Definition at line 5923 of file app_queue.c.

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

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

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

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

Definition at line 6305 of file app_queue.c.

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

Referenced by handle_queue_add_member().

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

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

Definition at line 6523 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_pause_member().

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

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

Definition at line 6435 of file app_queue.c.

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

Referenced by handle_queue_remove_member().

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

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

Definition at line 6656 of file app_queue.c.

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

Referenced by handle_queue_rule_show().

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

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

Definition at line 6593 of file app_queue.c.

References ast_strdup, and complete_queue().

Referenced by handle_queue_set_member_penalty().

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

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

Definition at line 5945 of file app_queue.c.

References complete_queue().

Referenced by queue_show().

05946 {
05947    if (pos == 2)
05948       return complete_queue(line, word, pos, state);
05949    return NULL;
05950 }

static int compress_char ( const char  c  )  [static]

Definition at line 872 of file app_queue.c.

Referenced by member_hash_fn().

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

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

Copy rule from global list into specified queue.

Definition at line 4786 of file app_queue.c.

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

Referenced by join_queue().

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

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

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

Definition at line 847 of file app_queue.c.

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

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

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

static void destroy_queue ( void *  obj  )  [static]

Free queue's member list then its string fields.

Definition at line 1452 of file app_queue.c.

References ao2_ref, ast_string_field_free_memory, free, free_members(), MAX_PERIODIC_ANNOUNCEMENTS, call_queue::members, and call_queue::sound_periodicannounce.

Referenced by alloc_queue().

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

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

Definition at line 820 of file app_queue.c.

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

Referenced by load_module().

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

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 2195 of file app_queue.c.

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

Referenced by ring_entry(), and wait_for_answer().

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

static void do_print ( struct mansession s,
int  fd,
const char *  str 
) [static]

direct ouput to manager or cli with proper terminator

Definition at line 5781 of file app_queue.c.

References ast_cli(), and astman_append().

Referenced by __queues_show().

05782 {
05783    if (s)
05784       astman_append(s, "%s\r\n", str);
05785    else
05786       ast_cli(fd, "%s\n", str);
05787 }

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

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, PM_MAX_LEN, and member::state_interface.

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

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

static void end_bridge_callback ( void *  data  )  [static]

Definition at line 3334 of file app_queue.c.

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

Referenced by try_calling().

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

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

Definition at line 3327 of file app_queue.c.

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

Referenced by try_calling().

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

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

find the entry with the best metric, or NULL

Definition at line 2407 of file app_queue.c.

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

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

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

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

Reload a single queue via realtime.

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

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

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

Definition at line 1490 of file app_queue.c.

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

Referenced by load_realtime_queue().

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

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

Iterate through queue's member list and delete them.

Definition at line 1434 of file app_queue.c.

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

Referenced by destroy_queue().

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

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

Definition at line 4433 of file app_queue.c.

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

Referenced by queue_function_memberpenalty_read().

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

static enum queue_member_status get_member_status ( struct call_queue q,
int  max_penalty,
int  min_penalty 
) [static]

Check if members are available.

This function checks to see if members are available to be called. If any member is available, the function immediately returns QUEUE_NORMAL. If no members are available, the appropriate reason why is returned

Definition at line 687 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL, and member::status.

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

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

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

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

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

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

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

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

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

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

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

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

Definition at line 6712 of file app_queue.c.

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

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

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

Definition at line 6678 of file app_queue.c.

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

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

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

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

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

static int handle_statechange ( void *  datap  )  [static]

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

Definition at line 789 of file app_queue.c.

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

Referenced by device_state_cb().

00790 {
00791    struct member_interface *curint;
00792    struct statechange *sc = datap;
00793    char interface[80];
00794 
00795    AST_LIST_LOCK(&interfaces);
00796    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00797       char *slash_pos;
00798       ast_copy_string(interface, curint->interface, sizeof(interface));
00799       if ((slash_pos = strchr(interface, '/')))
00800          if ((slash_pos = strchr(slash_pos + 1, '/')))
00801             *slash_pos = '\0';
00802       if (!strcasecmp(interface, sc->dev))
00803          break;
00804    }
00805    AST_LIST_UNLOCK(&interfaces);
00806 
00807    if (!curint) {
00808       ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, devstate2str(sc->state));
00809       ast_free(sc);
00810       return 0;
00811    }
00812 
00813    ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state));
00814 
00815    update_status(sc->dev, sc->state);
00816    ast_free(sc);
00817    return 0;
00818 }

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

Hang up a list of outgoing calls.

Definition at line 2091 of file app_queue.c.

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

Referenced by try_calling().

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

static void init_queue ( struct call_queue q  )  [static]

Initialize Queue default values.

Note:
the queue's lock must be held before executing this function

Definition at line 904 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_LINEAR, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

static void insert_entry ( struct call_queue q,
struct queue_ent prev,
struct queue_ent new,
int *  pos 
) [inline, static]

Insert the 'new' entry after the 'prev' entry of queue 'q'.

Definition at line 650 of file app_queue.c.

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

Referenced by join_queue().

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

static int insert_penaltychange ( const char *  list_name,
const char *  content,
const int  linenum 
) [static]

Change queue penalty by adding rule.

Check rule for errors with time or fomatting, see if rule is relative to rest of queue, iterate list of rules to find correct insertion point, insert and return.

Return values:
-1 on failure
0 on success
Note:
Call this with the rule_lists locked

Definition at line 1078 of file app_queue.c.

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

Referenced by reload_queue_rules().

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

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

Definition at line 568 of file app_queue.c.

References ARRAY_LEN, strategy::name, and strategies.

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

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

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

Definition at line 4126 of file app_queue.c.

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

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

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

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

Definition at line 1009 of file app_queue.c.

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

Referenced by remove_from_interfaces().

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

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

A simple process, really. Count the number of members who are available to take our call and then see if we are in a position in the queue at which a member could accept our call.

Parameters:
[in] qe The caller who wants to know if it is his turn
Return values:
0 It is not our turn
1 It is our turn

Definition at line 2901 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

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

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

Definition at line 1734 of file app_queue.c.

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

Referenced by queue_exec().

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

static void leave_queue ( struct queue_ent qe  )  [static]

Caller leaving queue.

Search the queue to find the leaving client, if found remove from queue create manager event, move others up the queue.

Definition at line 2034 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, manager_event, call_queue::name, queue_ent::next, queue_ent::parent, queue_ent::pos, queue_ent::qe_rules, queue_t_ref, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, SENTINEL, and var.

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

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

static int load_module ( void   )  [static]

Definition at line 6799 of file app_queue.c.

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

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

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

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

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

Definition at line 1624 of file app_queue.c.

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

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

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

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

Definition at line 6175 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 6260 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 6284 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 6336 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 5981 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 5971 of file app_queue.c.

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

Referenced by load_module().

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

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

Queue status info via AMI.

Definition at line 6080 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_t_unref, queues, RESULT_SUCCESS, S_OR, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by load_module().

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

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

Summary of queue info via the AMI.

Definition at line 6006 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 6226 of file app_queue.c.

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

Referenced by load_module().

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

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

Definition at line 894 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and member::interface.

Referenced by init_queue().

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

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

Definition at line 882 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

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

static int num_available_members ( struct call_queue q  )  [static]

Get the number of members available to accept a call.

Note:
The queue passed in should be locked prior to this function call
Parameters:
[in] q The queue for which we are couting the number of available members
Returns:
Return the number of available members in queue q

Definition at line 2115 of file app_queue.c.

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

Referenced by compare_weight(), and is_our_turn().

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

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

Definition at line 1814 of file app_queue.c.

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

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

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

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

PauseQueueMember application.

Definition at line 4562 of file app_queue.c.

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

Referenced by load_module().

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

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

QueueLog application.

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

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

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

Definition at line 599 of file app_queue.c.

References CMP_MATCH, CMP_STOP, and call_queue::name.

Referenced by load_module().

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

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

The starting point for all queue calls.

The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again unless some condition (such as an expiration time) causes us to exit the queue.

Definition at line 4830 of file app_queue.c.

References call_queue::announcefrequency, AST_APP_ARG, ast_cdr_noanswer(), ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::cdr, queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::min_penalty, queue_ent::moh, call_queue::name, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, queue_ent::prio, QUEUE_CONTINUE, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL, QUEUE_TIMEOUT, QUEUE_UNKNOWN, queue_unref(), record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), set_queue_variables(), queue_ent::start, status, stop, penalty_rule::time, try_calling(), update_qe_rule(), update_realtime_members(), url, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Definition at line 592 of file app_queue.c.

References ast_str_case_hash(), and call_queue::name.

Referenced by load_module().

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

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

Definition at line 617 of file app_queue.c.

References ao2_ref.

Referenced by insert_entry().

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

static void queue_set_param ( struct call_queue q,
const char *  param,
const char *  val,
int  linenum,
int  failunknown 
) [static]

Configure a queue parameter.

The failunknown flag is set for config files (and static realtime) to show errors for unknown parameters. It is cleared for dynamic realtime to allow extra fields in the tables.

Note:
For error reporting, line number is passed for .conf static configuration, for Realtime queues, linenum is -1.

Definition at line 1157 of file app_queue.c.

References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, buf, queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, queue_ent::moh, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

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

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

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 3233 of file app_queue.c.

References ast_free.

03234 {
03235    struct queue_transfer_ds *qtds = data;
03236    ast_free(qtds);
03237 }

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

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

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

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

Definition at line 623 of file app_queue.c.

References ao2_ref.

Referenced by queue_exec().

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

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

Definition at line 2015 of file app_queue.c.

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

Referenced by try_calling().

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

static void record_abandoned ( struct queue_ent qe  )  [static]

Record that a caller gave up on waiting in queue.

Definition at line 2570 of file app_queue.c.

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

Referenced by queue_exec(), and try_calling().

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

static int reload ( void   )  [static]

Definition at line 6854 of file app_queue.c.

References ast_unload_realtime(), and reload_queues().

06855 {
06856    ast_unload_realtime("queue_members");
06857    reload_queues(1);
06858    return 0;
06859 }

static void reload_queue_members ( void   )  [static]

Reload dynamic queue members persisted into the astdb.

Definition at line 4465 of file app_queue.c.

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

Referenced by load_module().

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

static int reload_queue_rules ( int  reload  )  [static]

Definition at line 5509 of file app_queue.c.

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

Referenced by handle_queue_rule_reload(), and reload_queues().

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

static int reload_queues ( int  reload  )  [static]

Definition at line 5555 of file app_queue.c.

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

Referenced by load_module(), and reload().

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

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

Definition at line 1037 of file app_queue.c.

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

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

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

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

Remove member from queue.

Return values:
RES_NOT_DYNAMIC when they aren't a RT member
RES_NOSUCHQUEUE queue does not exist
RES_OKAY removed member from queue
RES_EXISTS queue exists but no members

Definition at line 4199 of file app_queue.c.

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

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

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

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

Part 2 of ring_one.

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

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

Definition at line 2254 of file app_queue.c.

References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ao2_lock(), ao2_unlock(), ast_channel::appl, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_free, ast_request(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, manager_event, callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, status, callattempt::stillgoing, update_status(), ast_cdr::userfield, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

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

static int ring_one ( struct queue_ent qe,
struct callattempt outgoing,
int *  busies 
) [static]

Place a call to a queue member.

Once metrics have been calculated for each member, this function is used to place a call to the appropriate member (or members). The low-level channel-handling and error detection is handled in ring_entry

Return values:
1 if a member was called successfully
0 otherwise

Definition at line 2432 of file app_queue.c.

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

Referenced by try_calling(), and wait_for_answer().

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

static void rna ( int  rnatime,
struct queue_ent qe,
char *  interface,
char *  membername,
int  pause 
) [static]

RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.

Definition at line 2587 of file app_queue.c.

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

Referenced by wait_for_answer().

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

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

RemoveQueueMember application.

Definition at line 4634 of file app_queue.c.

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

Referenced by load_module().

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

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

Find rt member record to update otherwise create one.

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

Definition at line 1370 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and update_realtime_members().

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

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

Playback announcement to queued members if peroid has elapsed.

Definition at line 2517 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

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

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

Definition at line 1871 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, AST_CONTROL_RINGING, AST_DIGIT_ANY, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verb, queue_ent::chan, call_queue::holdtime, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, call_queue::name, queue_ent::parent, play_file(), queue_ent::pos, call_queue::queue_quantity1, call_queue::queue_quantity2, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, and valid_exit().

Referenced by queue_exec(), and wait_our_turn().

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

static void send_agent_complete ( const struct queue_ent qe,
const char *  queuename,
const struct ast_channel peer,
const struct member member,
time_t  callstart,
char *  vars,
size_t  vars_len,
enum agent_complete_reason  rsn 
) [static]

Send out AMI message with member call completion status information.

Definition at line 3190 of file app_queue.c.

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

Referenced by try_calling().

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

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

Definition at line 4307 of file app_queue.c.

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

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

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

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

Definition at line 4384 of file app_queue.c.

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

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

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

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

sets the QUEUESTATUS channel variable

Definition at line 556 of file app_queue.c.

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

Referenced by queue_exec().

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

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

Set variables of queue.

Definition at line 631 of file app_queue.c.

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

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

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

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

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

Definition at line 3294 of file app_queue.c.

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

Referenced by try_calling().

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

static int store_next_lin ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Search for best metric and add to Linear queue.

Definition at line 2493 of file app_queue.c.

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

Referenced by try_calling().

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

static int store_next_rr ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Search for best metric and add to Round Robbin queue.

Definition at line 2469 of file app_queue.c.

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

Referenced by try_calling().

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

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

Definition at line 580 of file app_queue.c.

References ARRAY_LEN, and strategies.

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

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

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

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

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

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

Definition at line 3376 of file app_queue.c.

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

Referenced by queue_exec().

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

static int unload_module ( void   )  [static]

Definition at line 6747 of file app_queue.c.

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

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

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

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

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

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

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

References ao2_find, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, call_queue::members, OBJ_POINTER, and queues.

Referenced by queue_transfer_fixup(), and try_calling().

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

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

Definition at line 1669 of file app_queue.c.

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

Referenced by set_member_paused().

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

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1683 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, queues, member::realtime, remove_from_interfaces(), rt_handle_member_record(), S_OR, SENTINEL, and member::state_interface.

Referenced by load_realtime_queue(), and queue_exec().

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

static int update_status ( const char *  interface,
const int  status 
) [static]

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

Lock interface list find sc, iterate through each queues queue_member list for member to update state inside queues

Definition at line 736 of file app_queue.c.

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

Referenced by handle_statechange(), and ring_entry().

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

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

UnPauseQueueMember application.

Definition at line 4598 of file app_queue.c.

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

Referenced by load_module().

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

static int valid_exit ( struct queue_ent qe,
char  digit 
) [static]

Check for valid exit from queue via goto.

Return values:
0 if failure
1 if successful

Definition at line 1838 of file app_queue.c.

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

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

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

static char* vars2manager ( struct ast_channel chan,
char *  vars,
size_t  len 
) [static]

convert "\n" to "\nVariable: " ready for manager to use

Definition at line 2203 of file app_queue.c.

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

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

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

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 4114 of file app_queue.c.

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

Referenced by queue_exec().

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

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

Wait for a member to answer the call.

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

Definition at line 2626 of file app_queue.c.

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

Referenced by try_calling().

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

static int wait_our_turn ( struct queue_ent qe,
int  ringing,
enum queue_result reason 
) [static]

The waiting areas for callers who are not actively calling members.

This function is one large loop. This function will return if a caller either exits the queue or it becomes that caller's turn to attempt calling queue members. Inside the loop, we service the caller with periodic announcements, holdtime announcements, etc. as configured in queues.conf

Return values:
0 if the caller's turn has arrived
-1 if the caller should exit the queue.

Definition at line 2974 of file app_queue.c.

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

Referenced by queue_exec().

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


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 6865 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 154 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 213 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 215 of file app_queue.c.

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

Definition at line 214 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 239 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 241 of file app_queue.c.

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

Definition at line 240 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 272 of file app_queue.c.

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

Definition at line 274 of file app_queue.c.

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

Definition at line 273 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 226 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 228 of file app_queue.c.

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

Definition at line 227 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 257 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 259 of file app_queue.c.

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

Definition at line 258 of file app_queue.c.

Definition at line 6865 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 294 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 6737 of file app_queue.c.

Referenced by load_module(), and unload_module().

char* descrip [static]

Definition at line 158 of file app_queue.c.

struct ast_event_sub* device_state_sub [static]

Subscription to device state change events.

Definition at line 303 of file app_queue.c.

Definition at line 138 of file app_queue.c.

Definition at line 320 of file app_queue.c.

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

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 297 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 280 of file app_queue.c.

const char qpm_cmd_usage[] [static]
Initial value:
 
"Usage: queue pause member <channel> in <queue> reason <reason>\n"

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

int queue_keep_stats = 0 [static]

queues.conf [general] option

Definition at line 285 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 288 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

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

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

Definition at line 5471 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 5458 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 5490 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 5499 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ao2_container* queues [static]

Definition at line 5440 of file app_queue.c.

Referenced by load_module(), and unload_module().

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

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 300 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

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

Definition at line 156 of file app_queue.c.

char* text
int update_cdr = 0 [static]

queues.conf [general] option

Definition at line 306 of file app_queue.c.

Referenced by login_exec().

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 291 of file app_queue.c.


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