True call queues with optional send URL on answer. More...
#include "asterisk.h"
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include <ctype.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/event.h"
#include "asterisk/astobj2.h"
#include "asterisk/strings.h"
#include "asterisk/global_datastores.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/aoc.h"
#include "asterisk/callerid.h"
#include "asterisk/cel.h"
#include "asterisk/data.h"
Go to the source code of this file.
Data Structures | |
struct | autopause |
struct | call_queue |
struct | callattempt |
We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More... | |
struct | member |
struct | penalty_rule |
struct | queue_end_bridge |
struct | queue_ent |
struct | queue_transfer_ds |
struct | rule_list |
struct | statechange |
struct | strategy |
Defines | |
#define | ANNOUNCEHOLDTIME_ALWAYS 1 |
#define | ANNOUNCEHOLDTIME_ONCE 2 |
#define | ANNOUNCEPOSITION_LIMIT 4 |
#define | ANNOUNCEPOSITION_MORE_THAN 3 |
#define | ANNOUNCEPOSITION_NO 2 |
#define | ANNOUNCEPOSITION_YES 1 |
#define | AST_MAX_WATCHERS 256 |
#define | DATA_EXPORT_CALL_QUEUE(MEMBER) |
#define | DATA_EXPORT_MEMBER(MEMBER) |
#define | DATA_EXPORT_QUEUE_ENT(MEMBER) |
#define | DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 |
#define | DEFAULT_RETRY 5 |
#define | DEFAULT_TIMEOUT 15 |
#define | MAX_PERIODIC_ANNOUNCEMENTS 10 |
#define | MAX_QUEUE_BUCKETS 53 |
#define | QUEUE_EVENT_VARIABLES 3 |
#define | queue_t_ref(a, b) queue_ref(a) |
#define | queue_t_unref(a, b) queue_unref(a) |
#define | queues_t_link(c, q, tag) ao2_t_link(c,q,tag) |
#define | queues_t_unlink(c, q, tag) ao2_t_unlink(c,q,tag) |
#define | RECHECK 1 |
#define | RES_EXISTS (-1) |
#define | RES_NOSUCHQUEUE (-3) |
#define | RES_NOT_DYNAMIC (-4) |
#define | RES_OKAY 0 |
#define | RES_OUTOFMEMORY (-2) |
Enumerations | |
enum | { QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM, QUEUE_STRATEGY_RRORDERED } |
enum | { QUEUE_AUTOPAUSE_OFF = 0, QUEUE_AUTOPAUSE_ON, QUEUE_AUTOPAUSE_ALL } |
enum | agent_complete_reason { CALLER, AGENT, TRANSFER } |
enum | empty_conditions { QUEUE_EMPTY_PENALTY = (1 << 0), QUEUE_EMPTY_PAUSED = (1 << 1), QUEUE_EMPTY_INUSE = (1 << 2), QUEUE_EMPTY_RINGING = (1 << 3), QUEUE_EMPTY_UNAVAILABLE = (1 << 4), QUEUE_EMPTY_INVALID = (1 << 5), QUEUE_EMPTY_UNKNOWN = (1 << 6), QUEUE_EMPTY_WRAPUP = (1 << 7) } |
enum | queue_reload_mask { QUEUE_RELOAD_PARAMETERS = (1 << 0), QUEUE_RELOAD_MEMBER = (1 << 1), QUEUE_RELOAD_RULES = (1 << 2), QUEUE_RESET_STATS = (1 << 3) } |
enum | queue_result { QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3, QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7 } |
enum | queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF } |
Functions | |
static char * | __queues_show (struct mansession *s, int fd, int argc, const char *const *argv) |
Show queue(s) status and statistics. | |
static 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_queue * | alloc_queue (const char *queuename) |
static int | aqm_exec (struct ast_channel *chan, const char *data) |
AddQueueMember application. | |
AST_DATA_STRUCTURE (queue_ent, DATA_EXPORT_QUEUE_ENT) | |
AST_DATA_STRUCTURE (member, DATA_EXPORT_MEMBER) | |
AST_DATA_STRUCTURE (call_queue, DATA_EXPORT_CALL_QUEUE) | |
static | AST_LIST_HEAD_STATIC (rule_lists, rule_list) |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"True Call Queueing",.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_DEVSTATE_CONSUMER,.nonoptreq="res_monitor",) | |
static int | attended_transfer_occurred (struct ast_channel *chan) |
mechanism to tell if a queue caller was atxferred by a queue member. | |
static int | autopause2int (const char *autopause) |
static int | calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp) |
Calculate the metric of each member in the outgoing callattempts. | |
static void | callattempt_free (struct callattempt *doomed) |
static int | can_ring_entry (struct queue_ent *qe, struct callattempt *call) |
static void | clear_queue (struct call_queue *q) |
static int | clear_stats (const char *queuename) |
Facilitates resetting statistics for a queue. | |
static int | compare_weight (struct call_queue *rq, struct member *member) |
static char * | complete_queue (const char *line, const char *word, int pos, int state, ptrdiff_t word_list_offset) |
Check if a given word is in a space-delimited list. | |
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 member * | create_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface) |
allocate space for new queue member and set fields based on parameters passed | |
static void | destroy_queue (void *obj) |
Free queue's member list then its string fields. | |
static void | device_state_cb (const struct ast_event *event, void *unused) |
static void | do_hang (struct callattempt *o) |
common hangup actions | |
static void | do_print (struct mansession *s, int fd, const char *str) |
direct ouput to manager or cli with proper terminator | |
static void | dump_queue_members (struct call_queue *pm_queue) |
Dump all members in a specific queue to the database. | |
static void | end_bridge_callback (void *data) |
static void | end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) |
static int | extension_state_cb (char *context, char *exten, enum ast_extension_states state, void *data) |
static int | extensionstate2devicestate (int state) |
Helper function which converts from extension state to device state values. | |
static struct callattempt * | find_best (struct callattempt *outgoing) |
find the entry with the best metric, or NULL | |
static struct call_queue * | find_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config) |
Reload a single queue via realtime. | |
static void | free_members (struct call_queue *q, int all) |
Iterate through queue's member list and delete them. | |
static int | get_member_penalty (char *queuename, char *interface) |
static int | get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions, int devstate) |
Check if members are available. | |
static int | get_queue_member_status (struct member *cur) |
Return the current state of a member. | |
static char * | handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_reset (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static int | handle_statechange (void *datap) |
set a member's status based on device state of that member's interface | |
static void | hangupcalls (struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere) |
Hang up a list of outgoing calls. | |
static void | init_queue (struct call_queue *q) |
Initialize Queue default values. | |
static void | insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos) |
Insert the 'new' entry after the 'prev' entry of queue 'q'. | |
static int | insert_penaltychange (const char *list_name, const char *content, const int linenum) |
Change queue penalty by adding rule. | |
static const char * | int2strat (int strategy) |
static struct member * | interface_exists (struct call_queue *q, const char *interface) |
static int | is_our_turn (struct queue_ent *qe) |
Check if we should start attempting to call queue members. | |
static int | join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, int position) |
static int | kill_dead_members (void *obj, void *arg, int flags) |
static int | kill_dead_queues (void *obj, void *arg, int flags) |
static void | leave_queue (struct queue_ent *qe) |
Caller leaving queue. | |
static int | load_module (void) |
static struct call_queue * | load_realtime_queue (const char *queuename) |
static int | manager_add_queue_member (struct mansession *s, const struct message *m) |
static int | manager_pause_queue_member (struct mansession *s, const struct message *m) |
static int | manager_queue_log_custom (struct mansession *s, const struct message *m) |
static int | manager_queue_member_penalty (struct mansession *s, const struct message *m) |
static int | manager_queue_reload (struct mansession *s, const struct message *m) |
static int | manager_queue_reset (struct mansession *s, const struct message *m) |
static int | manager_queue_rule_show (struct mansession *s, const struct message *m) |
static int | manager_queues_show (struct mansession *s, const struct message *m) |
static int | manager_queues_status (struct mansession *s, const struct message *m) |
Queue status info via AMI. | |
static int | manager_queues_summary (struct mansession *s, const struct message *m) |
Summary of queue info via the AMI. | |
static int | manager_remove_queue_member (struct mansession *s, const struct message *m) |
static int | mark_dead_and_unfound (void *obj, void *arg, int flags) |
static int | mark_member_dead (void *obj, void *arg, int flags) |
static void | member_add_to_queue (struct call_queue *queue, struct member *mem) |
static void | member_call_pending_clear (struct member *mem) |
static int | member_call_pending_set (struct member *mem) |
static int | member_cmp_fn (void *obj1, void *obj2, int flags) |
static int | member_hash_fn (const void *obj, const int flags) |
static void | member_remove_from_queue (struct call_queue *queue, struct member *mem) |
static int | member_status_available (int status) |
static int | num_available_members (struct call_queue *q) |
Get the number of members available to accept a call. | |
static void | parse_empty_options (const char *value, enum empty_conditions *empty, int joinempty) |
static int | play_file (struct ast_channel *chan, const char *filename) |
static int | pqm_exec (struct ast_channel *chan, const char *data) |
PauseQueueMember application. | |
static int | ql_exec (struct ast_channel *chan, const char *data) |
QueueLog application. | |
static int | queue_cmp_cb (void *obj, void *arg, int flags) |
static int | queue_delme_members_decrement_followers (void *obj, void *arg, int flag) |
static int | queue_exec (struct ast_channel *chan, const char *data) |
The starting point for all queue calls. | |
static int | queue_function_exists (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Check if a given queue exists. | |
static int | queue_function_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. | |
static int | queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value) |
Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. | |
static int | queue_function_qac (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Get number either busy / free / ready or total members of a specific queue. | |
static int | queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Get the total number of members in a specific queue (Deprecated). | |
static int | queue_function_queuememberlist (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue. | |
static int | queue_function_queuememberpaused (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
static int | queue_function_queuememberstatus (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
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 int | queue_member_decrement_followers (void *obj, void *arg, int flag) |
static void | queue_member_follower_removal (struct call_queue *queue, struct member *mem) |
static struct call_queue * | queue_ref (struct call_queue *q) |
static void | queue_set_global_params (struct ast_config *cfg) |
static void | queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown) |
Configure a queue parameter. | |
static char * | queue_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static void | queue_transfer_destroy (void *data) |
static void | queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) |
Log an attended transfer when a queue caller channel is masqueraded. | |
static struct call_queue * | queue_unref (struct call_queue *q) |
static int | queues_data_provider_get (const struct ast_data_search *search, struct ast_data *data_root) |
static void | queues_data_provider_get_helper (const struct ast_data_search *search, struct ast_data *data_root, struct call_queue *queue) |
static void | recalc_holdtime (struct queue_ent *qe, int newholdtime) |
static void | record_abandoned (struct queue_ent *qe) |
Record that a caller gave up on waiting in queue. | |
static int | reload (void) |
static int | reload_handler (int reload, struct ast_flags *mask, const char *queuename) |
The command center for all reload operations. | |
static void | reload_queue_members (void) |
Reload dynamic queue members persisted into the astdb. | |
static int | reload_queue_rules (int reload) |
Reload the rules defined in queuerules.conf. | |
static int | reload_queues (int reload, struct ast_flags *mask, const char *queuename) |
reload the queues.conf file | |
static void | reload_single_member (const char *memberdata, struct call_queue *q) |
reload information pertaining to a single member | |
static void | reload_single_queue (struct ast_config *cfg, struct ast_flags *mask, const char *queuename) |
Reload information pertaining to a particular queue. | |
static int | remove_from_queue (const char *queuename, const char *interface) |
Remove member from queue. | |
static int | remove_members_and_mark_unfound (void *obj, void *arg, int flags) |
static int | ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies) |
Part 2 of ring_one. | |
static int | ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies) |
Place a call to a queue member. | |
static void | rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause) |
RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. | |
static int | rqm_exec (struct ast_channel *chan, const char *data) |
RemoveQueueMember application. | |
static void | rt_handle_member_record (struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface) |
Find rt member record to update otherwise create one. | |
static int | say_periodic_announcement (struct queue_ent *qe, int ringing) |
Playback announcement to queued members if period has elapsed. | |
static int | say_position (struct queue_ent *qe, int ringing) |
static void | send_agent_complete (const struct queue_ent *qe, const char *queuename, const struct ast_channel *peer, const struct member *member, time_t callstart, char *vars, size_t vars_len, enum agent_complete_reason rsn) |
Send out AMI message with member call completion status information. | |
static int | set_member_paused (const char *queuename, const char *interface, const char *reason, int paused) |
static int | set_member_penalty (const char *queuename, const char *interface, int penalty) |
static void | set_queue_result (struct ast_channel *chan, enum queue_result res) |
sets the QUEUESTATUS channel variable | |
static void | set_queue_variables (struct call_queue *q, struct ast_channel *chan) |
Set variables of queue. | |
static struct ast_datastore * | setup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl) |
create a datastore for storing relevant info to log attended transfers in the queue_log | |
static int | store_next_lin (struct queue_ent *qe, struct callattempt *outgoing) |
Search for best metric and add to Linear queue. | |
static int | store_next_rr (struct queue_ent *qe, struct callattempt *outgoing) |
Search for best metric and add to Round Robbin queue. | |
static int | strat2int (const char *strategy) |
static int | try_calling (struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing) |
A large function which calls members, updates statistics, and bridges the caller and a member. | |
static int | unload_module (void) |
static void | update_qe_rule (struct queue_ent *qe) |
update rules for queues | |
static int | update_queue (struct call_queue *q, struct member *member, int callcompletedinsl, int newtalktime) |
update the queue status | |
static int | update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value) |
static void | update_realtime_members (struct call_queue *q) |
static int | update_status (struct call_queue *q, struct member *m, const int status) |
set a member's status based on device state of that member's state_interface. | |
static int | upqm_exec (struct ast_channel *chan, const char *data) |
UnPauseQueueMember application. | |
static int | valid_exit (struct queue_ent *qe, char digit) |
Check for valid exit from queue via goto. | |
static char * | vars2manager (struct ast_channel *chan, char *vars, size_t len) |
convert "\n" to "\nVariable: " ready for manager to use | |
static int | wait_a_bit (struct queue_ent *qe) |
static struct callattempt * | wait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed, int ringing) |
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. | |
static int | word_in_list (const char *list, const char *word) |
Check if a given word is in a space-delimited list. | |
Variables | |
static char * | app = "Queue" |
static char * | app_aqm = "AddQueueMember" |
static char * | app_pqm = "PauseQueueMember" |
static char * | app_ql = "QueueLog" |
static char * | app_rqm = "RemoveQueueMember" |
static char * | app_upqm = "UnpauseQueueMember" |
static int | autofill_default = 1 |
queues.conf [general] option | |
static struct autopause | autopausesmodes [] |
static struct ast_cli_entry | cli_queue [] |
static struct ast_event_sub * | device_state_sub |
Subscription to device state change events. | |
static struct ast_taskprocessor * | devicestate_tps |
static int | montype_default = 0 |
queues.conf [general] option | |
static const char *const | pm_family = "Queue/PersistentMembers" |
Persistent Members astdb family. | |
static const char | qpm_cmd_usage [] |
static const char | qsmp_cmd_usage [] |
static struct ast_data_entry | queue_data_providers [] |
static int | queue_debug = 0 |
queues.conf [general] extra debug 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 | queueexists_function |
static struct ast_custom_function | queuemembercount_dep |
static struct ast_custom_function | queuemembercount_function |
static struct ast_custom_function | queuememberlist_function |
static struct ast_custom_function | queuememberpaused_function |
static struct ast_custom_function | queuememberpenalty_function |
static struct ast_custom_function | queuememberstatus_function |
static struct ao2_container * | queues |
static struct ast_data_handler | queues_data_provider |
static struct ast_custom_function | queuevar_function |
static struct ast_custom_function | queuewaitingcount_function |
static const char | qum_cmd_usage [] |
static int | shared_lastcall = 1 |
queues.conf [general] option | |
static struct strategy | strategies [] |
static int | update_cdr = 0 |
queues.conf [general] option | |
static int | use_weight = 0 |
queues.conf per-queue weight option |
True call queues with optional send URL on answer.
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.
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 ANNOUNCEHOLDTIME_ALWAYS 1 |
Definition at line 1204 of file app_queue.c.
Referenced by queue_set_param().
#define ANNOUNCEHOLDTIME_ONCE 2 |
Definition at line 1205 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 1220 of file app_queue.c.
Referenced by queue_set_param(), queues_data_provider_get_helper(), and say_position().
#define ANNOUNCEPOSITION_MORE_THAN 3 |
We say "Currently there are more than <limit>"
Definition at line 1219 of file app_queue.c.
Referenced by queue_set_param(), queues_data_provider_get_helper(), and say_position().
#define ANNOUNCEPOSITION_NO 2 |
We don't announce position
Definition at line 1218 of file app_queue.c.
Referenced by queue_set_param(), and queues_data_provider_get_helper().
#define ANNOUNCEPOSITION_YES 1 |
We announce position
Definition at line 1217 of file app_queue.c.
Referenced by init_queue(), queue_set_param(), queues_data_provider_get_helper(), and say_position().
#define AST_MAX_WATCHERS 256 |
Definition at line 3771 of file app_queue.c.
#define DATA_EXPORT_CALL_QUEUE | ( | MEMBER | ) |
Definition at line 8791 of file app_queue.c.
#define DATA_EXPORT_MEMBER | ( | MEMBER | ) |
Definition at line 8856 of file app_queue.c.
#define DATA_EXPORT_QUEUE_ENT | ( | MEMBER | ) |
Definition at line 8870 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 1025 of file app_queue.c.
Referenced by init_queue().
#define DEFAULT_RETRY 5 |
Definition at line 1021 of file app_queue.c.
Referenced by init_queue(), and queue_set_param().
#define DEFAULT_TIMEOUT 15 |
Definition at line 1022 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 1024 of file app_queue.c.
Referenced by destroy_queue(), init_queue(), and queue_set_param().
#define MAX_QUEUE_BUCKETS 53 |
Definition at line 1027 of file app_queue.c.
Referenced by load_module().
#define QUEUE_EVENT_VARIABLES 3 |
Definition at line 1206 of file app_queue.c.
Referenced by queue_set_param(), ring_entry(), rna(), send_agent_complete(), and try_calling().
#define queue_t_ref | ( | a, | |||
b | ) | queue_ref(a) |
Definition at line 1468 of file app_queue.c.
Referenced by leave_queue(), and try_calling().
#define queue_t_unref | ( | a, | |||
b | ) | queue_unref(a) |
Definition at line 1469 of file app_queue.c.
Referenced by __queues_show(), add_to_queue(), alloc_queue(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), extension_state_cb(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), join_queue(), leave_queue(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_exists(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuememberpaused(), queue_function_queuememberstatus(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().
#define queues_t_link | ( | c, | |||
q, | |||||
tag | ) | ao2_t_link(c,q,tag) |
Definition at line 1470 of file app_queue.c.
Referenced by find_queue_by_name_rt(), and reload_single_queue().
#define queues_t_unlink | ( | c, | |||
q, | |||||
tag | ) | ao2_t_unlink(c,q,tag) |
Definition at line 1471 of file app_queue.c.
Referenced by find_queue_by_name_rt(), leave_queue(), and unload_module().
#define RECHECK 1 |
Recheck every second to see we we're at the top yet
Definition at line 1023 of file app_queue.c.
Referenced by wait_our_turn().
#define RES_EXISTS (-1) |
Entry already exists
Definition at line 1030 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_NOSUCHQUEUE (-3) |
No such queue
Definition at line 1032 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_NOT_DYNAMIC (-4) |
Member is not dynamic
Definition at line 1033 of file app_queue.c.
Referenced by handle_queue_add_member(), handle_queue_remove_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_OKAY 0 |
Action completed
Definition at line 1029 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_OUTOFMEMORY (-2) |
Out of memory
Definition at line 1031 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), and reload_queue_members().
anonymous enum |
QUEUE_STRATEGY_RINGALL | |
QUEUE_STRATEGY_LEASTRECENT | |
QUEUE_STRATEGY_FEWESTCALLS | |
QUEUE_STRATEGY_RANDOM | |
QUEUE_STRATEGY_RRMEMORY | |
QUEUE_STRATEGY_LINEAR | |
QUEUE_STRATEGY_WRANDOM | |
QUEUE_STRATEGY_RRORDERED |
Definition at line 970 of file app_queue.c.
00970 { 00971 QUEUE_STRATEGY_RINGALL = 0, 00972 QUEUE_STRATEGY_LEASTRECENT, 00973 QUEUE_STRATEGY_FEWESTCALLS, 00974 QUEUE_STRATEGY_RANDOM, 00975 QUEUE_STRATEGY_RRMEMORY, 00976 QUEUE_STRATEGY_LINEAR, 00977 QUEUE_STRATEGY_WRANDOM, 00978 QUEUE_STRATEGY_RRORDERED, 00979 };
anonymous enum |
Definition at line 981 of file app_queue.c.
00981 { 00982 QUEUE_AUTOPAUSE_OFF = 0, 00983 QUEUE_AUTOPAUSE_ON, 00984 QUEUE_AUTOPAUSE_ALL 00985 };
Definition at line 4626 of file app_queue.c.
enum empty_conditions |
QUEUE_EMPTY_PENALTY | |
QUEUE_EMPTY_PAUSED | |
QUEUE_EMPTY_INUSE | |
QUEUE_EMPTY_RINGING | |
QUEUE_EMPTY_UNAVAILABLE | |
QUEUE_EMPTY_INVALID | |
QUEUE_EMPTY_UNKNOWN | |
QUEUE_EMPTY_WRAPUP |
Definition at line 1192 of file app_queue.c.
01192 { 01193 QUEUE_EMPTY_PENALTY = (1 << 0), 01194 QUEUE_EMPTY_PAUSED = (1 << 1), 01195 QUEUE_EMPTY_INUSE = (1 << 2), 01196 QUEUE_EMPTY_RINGING = (1 << 3), 01197 QUEUE_EMPTY_UNAVAILABLE = (1 << 4), 01198 QUEUE_EMPTY_INVALID = (1 << 5), 01199 QUEUE_EMPTY_UNKNOWN = (1 << 6), 01200 QUEUE_EMPTY_WRAPUP = (1 << 7), 01201 };
enum queue_reload_mask |
Definition at line 987 of file app_queue.c.
00987 { 00988 QUEUE_RELOAD_PARAMETERS = (1 << 0), 00989 QUEUE_RELOAD_MEMBER = (1 << 1), 00990 QUEUE_RELOAD_RULES = (1 << 2), 00991 QUEUE_RESET_STATS = (1 << 3), 00992 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL | |
QUEUE_CONTINUE |
Definition at line 1074 of file app_queue.c.
01074 { 01075 QUEUE_UNKNOWN = 0, 01076 QUEUE_TIMEOUT = 1, 01077 QUEUE_JOINEMPTY = 2, 01078 QUEUE_LEAVEEMPTY = 3, 01079 QUEUE_JOINUNAVAIL = 4, 01080 QUEUE_LEAVEUNAVAIL = 5, 01081 QUEUE_FULL = 6, 01082 QUEUE_CONTINUE = 7, 01083 };
Definition at line 1099 of file app_queue.c.
01099 { 01100 TIMEOUT_PRIORITY_APP, 01101 TIMEOUT_PRIORITY_CONF, 01102 };
static char* __queues_show | ( | struct mansession * | s, | |
int | fd, | |||
int | argc, | |||
const char *const * | argv | |||
) | [static] |
Show queue(s) status and statistics.
List the queues strategy, calls processed, members logged in, other queue statistics such as avg hold time.
Definition at line 7589 of file app_queue.c.
References ao2_container_count(), ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_devstate2str(), ast_load_realtime_multientry(), ast_str_alloca, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_strlen_zero(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, CLI_SHOWUSAGE, CLI_SUCCESS, call_queue::count, do_print(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_t_unref, queues, member::realtime, call_queue::realtime, call_queue::ringlimit, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
07590 { 07591 struct call_queue *q; 07592 struct ast_str *out = ast_str_alloca(240); 07593 int found = 0; 07594 time_t now = time(NULL); 07595 struct ao2_iterator queue_iter; 07596 struct ao2_iterator mem_iter; 07597 07598 if (argc != 2 && argc != 3) 07599 return CLI_SHOWUSAGE; 07600 07601 if (argc == 3) { /* specific queue */ 07602 if ((q = load_realtime_queue(argv[2]))) { 07603 queue_t_unref(q, "Done with temporary pointer"); 07604 } 07605 } else if (ast_check_realtime("queues")) { 07606 /* This block is to find any queues which are defined in realtime but 07607 * which have not yet been added to the in-core container 07608 */ 07609 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 07610 char *queuename; 07611 if (cfg) { 07612 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 07613 if ((q = load_realtime_queue(queuename))) { 07614 queue_t_unref(q, "Done with temporary pointer"); 07615 } 07616 } 07617 ast_config_destroy(cfg); 07618 } 07619 } 07620 07621 ao2_lock(queues); 07622 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK); 07623 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07624 float sl; 07625 struct call_queue *realtime_queue = NULL; 07626 07627 ao2_lock(q); 07628 /* This check is to make sure we don't print information for realtime 07629 * queues which have been deleted from realtime but which have not yet 07630 * been deleted from the in-core container. Only do this if we're not 07631 * looking for a specific queue. 07632 */ 07633 if (argc < 3 && q->realtime) { 07634 realtime_queue = load_realtime_queue(q->name); 07635 if (!realtime_queue) { 07636 ao2_unlock(q); 07637 queue_t_unref(q, "Done with iterator"); 07638 continue; 07639 } 07640 queue_t_unref(realtime_queue, "Queue is already in memory"); 07641 } 07642 07643 if (argc == 3 && strcasecmp(q->name, argv[2])) { 07644 ao2_unlock(q); 07645 queue_t_unref(q, "Done with iterator"); 07646 continue; 07647 } 07648 found = 1; 07649 07650 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count); 07651 if (q->maxlen) 07652 ast_str_append(&out, 0, "%d", q->maxlen); 07653 else 07654 ast_str_append(&out, 0, "unlimited"); 07655 sl = 0; 07656 if (q->callscompleted > 0) 07657 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 07658 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds", 07659 int2strat(q->strategy), q->holdtime, q->talktime, q->ringlimit, q->weight, 07660 q->callscompleted, q->callsabandoned,sl,q->servicelevel); 07661 do_print(s, fd, ast_str_buffer(out)); 07662 if (!ao2_container_count(q->members)) 07663 do_print(s, fd, " No Members"); 07664 else { 07665 struct member *mem; 07666 07667 do_print(s, fd, " Members: "); 07668 mem_iter = ao2_iterator_init(q->members, 0); 07669 while ((mem = ao2_iterator_next(&mem_iter))) { 07670 ast_str_set(&out, 0, " %s", mem->membername); 07671 if (strcasecmp(mem->membername, mem->interface)) { 07672 ast_str_append(&out, 0, " (%s)", mem->interface); 07673 } 07674 if (mem->penalty) 07675 ast_str_append(&out, 0, " with penalty %d", mem->penalty); 07676 ast_str_append(&out, 0, "%s%s%s (%s)", 07677 mem->dynamic ? " (dynamic)" : "", 07678 mem->realtime ? " (realtime)" : "", 07679 mem->paused ? " (paused)" : "", 07680 ast_devstate2str(mem->status)); 07681 if (mem->calls) 07682 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)", 07683 mem->calls, (long) (time(NULL) - mem->lastcall)); 07684 else 07685 ast_str_append(&out, 0, " has taken no calls yet"); 07686 do_print(s, fd, ast_str_buffer(out)); 07687 ao2_ref(mem, -1); 07688 } 07689 ao2_iterator_destroy(&mem_iter); 07690 } 07691 if (!q->head) 07692 do_print(s, fd, " No Callers"); 07693 else { 07694 struct queue_ent *qe; 07695 int pos = 1; 07696 07697 do_print(s, fd, " Callers: "); 07698 for (qe = q->head; qe; qe = qe->next) { 07699 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)", 07700 pos++, qe->chan->name, (long) (now - qe->start) / 60, 07701 (long) (now - qe->start) % 60, qe->prio); 07702 do_print(s, fd, ast_str_buffer(out)); 07703 } 07704 } 07705 do_print(s, fd, ""); /* blank line between entries */ 07706 ao2_unlock(q); 07707 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */ 07708 } 07709 ao2_iterator_destroy(&queue_iter); 07710 ao2_unlock(queues); 07711 if (!found) { 07712 if (argc == 3) 07713 ast_str_set(&out, 0, "No such queue: %s.", argv[2]); 07714 else 07715 ast_str_set(&out, 0, "No queues."); 07716 do_print(s, fd, ast_str_buffer(out)); 07717 } 07718 return CLI_SUCCESS; 07719 }
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.
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 |
Definition at line 5759 of file app_queue.c.
References 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, member_add_to_queue(), member::membername, member::paused, member::penalty, queue_t_unref, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and member::status.
Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().
05760 { 05761 struct call_queue *q; 05762 struct member *new_member, *old_member; 05763 int res = RES_NOSUCHQUEUE; 05764 05765 /*! \note Ensure the appropriate realtime queue is loaded. Note that this 05766 * short-circuits if the queue is already in memory. */ 05767 if (!(q = load_realtime_queue(queuename))) 05768 return res; 05769 05770 ao2_lock(q); 05771 if ((old_member = interface_exists(q, interface)) == NULL) { 05772 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) { 05773 new_member->dynamic = 1; 05774 member_add_to_queue(q, new_member); 05775 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 05776 "Queue: %s\r\n" 05777 "Location: %s\r\n" 05778 "MemberName: %s\r\n" 05779 "Membership: %s\r\n" 05780 "Penalty: %d\r\n" 05781 "CallsTaken: %d\r\n" 05782 "LastCall: %d\r\n" 05783 "Status: %d\r\n" 05784 "Paused: %d\r\n", 05785 q->name, new_member->interface, new_member->membername, 05786 "dynamic", 05787 new_member->penalty, new_member->calls, (int) new_member->lastcall, 05788 new_member->status, new_member->paused); 05789 05790 ao2_ref(new_member, -1); 05791 new_member = NULL; 05792 05793 if (dump) 05794 dump_queue_members(q); 05795 05796 res = RES_OKAY; 05797 } else { 05798 res = RES_OUTOFMEMORY; 05799 } 05800 } else { 05801 ao2_ref(old_member, -1); 05802 res = RES_EXISTS; 05803 } 05804 ao2_unlock(q); 05805 queue_t_unref(q, "Expiring temporary reference"); 05806 05807 return res; 05808 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static, read] |
Definition at line 2414 of file app_queue.c.
References ao2_t_alloc, ast_string_field_init, ast_string_field_set, destroy_queue(), and queue_t_unref.
Referenced by find_queue_by_name_rt(), and reload_single_queue().
02415 { 02416 struct call_queue *q; 02417 02418 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) { 02419 if (ast_string_field_init(q, 64)) { 02420 queue_t_unref(q, "String field allocation failed"); 02421 return NULL; 02422 } 02423 ast_string_field_set(q, name, queuename); 02424 } 02425 return q; 02426 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
AddQueueMember application.
Definition at line 6191 of file app_queue.c.
References add_to_queue(), args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.
Referenced by load_module().
06192 { 06193 int res=-1; 06194 char *parse, *temppos = NULL; 06195 AST_DECLARE_APP_ARGS(args, 06196 AST_APP_ARG(queuename); 06197 AST_APP_ARG(interface); 06198 AST_APP_ARG(penalty); 06199 AST_APP_ARG(options); 06200 AST_APP_ARG(membername); 06201 AST_APP_ARG(state_interface); 06202 ); 06203 int penalty = 0; 06204 06205 if (ast_strlen_zero(data)) { 06206 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n"); 06207 return -1; 06208 } 06209 06210 parse = ast_strdupa(data); 06211 06212 AST_STANDARD_APP_ARGS(args, parse); 06213 06214 if (ast_strlen_zero(args.interface)) { 06215 args.interface = ast_strdupa(chan->name); 06216 temppos = strrchr(args.interface, '-'); 06217 if (temppos) 06218 *temppos = '\0'; 06219 } 06220 06221 if (!ast_strlen_zero(args.penalty)) { 06222 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) { 06223 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 06224 penalty = 0; 06225 } 06226 } 06227 06228 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) { 06229 case RES_OKAY: 06230 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 06231 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 06232 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 06233 res = 0; 06234 break; 06235 case RES_EXISTS: 06236 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 06237 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 06238 res = 0; 06239 break; 06240 case RES_NOSUCHQUEUE: 06241 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 06242 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 06243 res = 0; 06244 break; 06245 case RES_OUTOFMEMORY: 06246 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 06247 break; 06248 } 06249 06250 return res; 06251 }
AST_DATA_STRUCTURE | ( | queue_ent | , | |
DATA_EXPORT_QUEUE_ENT | ||||
) |
AST_DATA_STRUCTURE | ( | member | , | |
DATA_EXPORT_MEMBER | ||||
) |
AST_DATA_STRUCTURE | ( | call_queue | , | |
DATA_EXPORT_CALL_QUEUE | ||||
) |
static AST_LIST_HEAD_STATIC | ( | rule_lists | , | |
rule_list | ||||
) | [static] |
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_LOAD_ORDER | , | |||
"True Call Queueing" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | reload = reload , |
|||
. | load_pri = AST_MODPRI_DEVSTATE_CONSUMER , |
|||
. | nonoptreq = "res_monitor" | |||
) |
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.
Definition at line 4731 of file app_queue.c.
References ast_channel_datastore_find().
Referenced by try_calling().
04732 { 04733 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 04734 }
static int autopause2int | ( | const char * | autopause | ) | [static] |
Definition at line 1373 of file app_queue.c.
References ARRAY_LEN, ast_strlen_zero(), ast_true(), autopausesmodes, QUEUE_AUTOPAUSE_OFF, and QUEUE_AUTOPAUSE_ON.
Referenced by queue_set_param().
01374 { 01375 int x; 01376 /*This 'double check' that default value is OFF */ 01377 if (ast_strlen_zero(autopause)) 01378 return QUEUE_AUTOPAUSE_OFF; 01379 01380 /*This 'double check' is to ensure old values works */ 01381 if(ast_true(autopause)) 01382 return QUEUE_AUTOPAUSE_ON; 01383 01384 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) { 01385 if (!strcasecmp(autopause, autopausesmodes[x].name)) 01386 return autopausesmodes[x].autopause; 01387 } 01388 01389 /*This 'double check' that default value is OFF */ 01390 return QUEUE_AUTOPAUSE_OFF; 01391 }
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
-1 | if penalties are exceeded | |
0 | otherwise |
Definition at line 4550 of file app_queue.c.
References ao2_container_count(), ast_debug, ast_log(), ast_random(), member::calls, member::interface, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, call_queue::members, callattempt::metric, queue_ent::min_penalty, option_debug, member::penalty, call_queue::penaltymemberslimit, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, QUEUE_STRATEGY_WRANDOM, member::queuepos, member::ringcount, call_queue::ringlimit, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
Referenced by try_calling().
04551 { 04552 /* disregarding penalty on too few members? */ 04553 int membercount = ao2_container_count(q->members); 04554 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1; 04555 04556 if (usepenalty) { 04557 if ((qe->max_penalty != INT_MAX && mem->penalty > qe->max_penalty) || 04558 (qe->min_penalty != INT_MAX && mem->penalty < qe->min_penalty)) { 04559 return -1; 04560 } 04561 } else { 04562 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n", 04563 membercount, q->penaltymemberslimit); 04564 } 04565 04566 switch (q->strategy) { 04567 case QUEUE_STRATEGY_RINGALL: 04568 /* Everyone equal, except for penalty */ 04569 tmp->metric = mem->penalty * 1000000 * usepenalty; 04570 break; 04571 case QUEUE_STRATEGY_LINEAR: 04572 if (pos < qe->linpos) { 04573 tmp->metric = 1000 + pos; 04574 } else { 04575 if (pos > qe->linpos) 04576 /* Indicate there is another priority */ 04577 qe->linwrapped = 1; 04578 tmp->metric = pos; 04579 } 04580 tmp->metric += mem->penalty * 1000000 * usepenalty; 04581 break; 04582 case QUEUE_STRATEGY_RRORDERED: 04583 case QUEUE_STRATEGY_RRMEMORY: 04584 pos = mem->queuepos; 04585 if (pos < q->rrpos) { 04586 tmp->metric = 1000 + pos; 04587 } else { 04588 if (pos > q->rrpos) 04589 /* Indicate there is another priority */ 04590 q->wrapped = 1; 04591 tmp->metric = pos; 04592 } 04593 tmp->metric += mem->penalty * 1000000 * usepenalty; 04594 break; 04595 case QUEUE_STRATEGY_RANDOM: 04596 tmp->metric = ast_random() % 1000; 04597 tmp->metric += mem->penalty * 1000000 * usepenalty; 04598 break; 04599 case QUEUE_STRATEGY_WRANDOM: 04600 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000); 04601 break; 04602 case QUEUE_STRATEGY_FEWESTCALLS: 04603 tmp->metric = mem->calls; 04604 tmp->metric += mem->penalty * 1000000 * usepenalty; 04605 break; 04606 case QUEUE_STRATEGY_LEASTRECENT: 04607 if (!mem->lastcall) 04608 tmp->metric = 0; 04609 else 04610 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 04611 tmp->metric += mem->penalty * 1000000 * usepenalty; 04612 break; 04613 default: 04614 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 04615 break; 04616 } 04617 if (q->ringlimit && (mem->ringcount >= q->ringlimit)) { 04618 tmp->metric += (mem->ringcount / q->ringlimit) * 10000000; 04619 } 04620 if (option_debug) 04621 ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 04622 tmp->metric, mem->interface, mem->ringcount, q->ringlimit); 04623 return 0; 04624 }
static void callattempt_free | ( | struct callattempt * | doomed | ) | [static] |
Definition at line 3076 of file app_queue.c.
References ao2_ref, ast_free, ast_party_connected_line_free(), callattempt::connected, and callattempt::member.
Referenced by hangupcalls(), and try_calling().
static int can_ring_entry | ( | struct queue_ent * | qe, | |
struct callattempt * | call | |||
) | [static] |
Definition at line 3296 of file app_queue.c.
References ast_debug, ast_log(), compare_weight(), get_queue_member_status(), callattempt::interface, callattempt::lastcall, callattempt::lastqueue, LOG_NOTICE, callattempt::member, member_call_pending_clear(), member_call_pending_set(), member_status_available(), queue_ent::parent, member::paused, call_queue::ringinuse, member::status, and call_queue::wrapuptime.
Referenced by ring_entry().
03297 { 03298 if (call->member->paused) { 03299 if (queue_debug) 03300 ast_log(LOG_NOTICE, "%s paused, can't receive call\n", call->interface); 03301 return 0; 03302 } 03303 03304 if (!qe->parent->ringinuse && !member_status_available(call->member->status)) { 03305 if (queue_debug) 03306 ast_log(LOG_NOTICE, "%s not available, can't receive call\n", call->interface); 03307 return 0; 03308 } 03309 03310 if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime)) 03311 || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) { 03312 if (queue_debug) 03313 ast_log(LOG_NOTICE, "Wrapuptime not yet expired on queue %s for %s\n", 03314 (call->lastqueue ? call->lastqueue->name : qe->parent->name), 03315 call->interface); 03316 return 0; 03317 } 03318 03319 if (use_weight && compare_weight(qe->parent, call->member)) { 03320 if (queue_debug) 03321 ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", 03322 qe->parent->name, call->interface); 03323 return 0; 03324 } 03325 03326 if (!qe->parent->ringinuse) { 03327 if (member_call_pending_set(call->member)) { 03328 if (queue_debug) 03329 ast_log(LOG_NOTICE, "%s has another call pending, can't receive call\n", 03330 call->interface); 03331 return 0; 03332 } 03333 03334 /* 03335 * The queue member is available. Get current status to be sure 03336 * because the device state and extension state callbacks may 03337 * not have updated the status yet. 03338 */ 03339 if (!member_status_available(get_queue_member_status(call->member))) { 03340 ast_debug(1, "%s actually not available, can't receive call\n", 03341 call->interface); 03342 member_call_pending_clear(call->member); 03343 return 0; 03344 } 03345 } 03346 03347 return 1; 03348 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 1939 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, member::lastcall, call_queue::members, and call_queue::talktime.
Referenced by clear_stats(), and find_queue_by_name_rt().
01940 { 01941 q->holdtime = 0; 01942 q->callscompleted = 0; 01943 q->callsabandoned = 0; 01944 q->callscompletedinsl = 0; 01945 q->talktime = 0; 01946 01947 if (q->members) { 01948 struct member *mem; 01949 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01950 while ((mem = ao2_iterator_next(&mem_iter))) { 01951 mem->calls = 0; 01952 mem->lastcall = 0; 01953 ao2_ref(mem, -1); 01954 } 01955 ao2_iterator_destroy(&mem_iter); 01956 } 01957 }
static int clear_stats | ( | const char * | queuename | ) | [static] |
Facilitates resetting statistics for a queue.
This function actually does not reset any statistics, but rather finds a call_queue struct which corresponds to the passed-in queue name and passes that structure to the clear_queue function. If no queuename is passed in, then all queues will have their statistics reset.
queuename | The name of the queue to reset the statistics for. If this is NULL or zero-length, then this means to reset the statistics for all queues |
void |
Definition at line 7528 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_t_iterator_next, ao2_unlock, ast_strlen_zero(), clear_queue(), queue_t_unref, and queues.
Referenced by reload_handler().
07529 { 07530 struct call_queue *q; 07531 struct ao2_iterator queue_iter; 07532 07533 queue_iter = ao2_iterator_init(queues, 0); 07534 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07535 ao2_lock(q); 07536 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) 07537 clear_queue(q); 07538 ao2_unlock(q); 07539 queue_t_unref(q, "Done with iterator"); 07540 } 07541 ao2_iterator_destroy(&queue_iter); 07542 return 0; 07543 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 3159 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, num_available_members(), OBJ_POINTER, queue_t_unref, queues, and call_queue::weight.
Referenced by can_ring_entry().
03160 { 03161 struct call_queue *q; 03162 struct member *mem; 03163 int found = 0; 03164 struct ao2_iterator queue_iter; 03165 03166 queue_iter = ao2_iterator_init(queues, 0); 03167 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 03168 if (q == rq) { /* don't check myself, could deadlock */ 03169 queue_t_unref(q, "Done with iterator"); 03170 continue; 03171 } 03172 ao2_lock(q); 03173 if (q->count && q->members) { 03174 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 03175 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 03176 if (q->weight > rq->weight && q->count >= num_available_members(q)) { 03177 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); 03178 found = 1; 03179 } 03180 ao2_ref(mem, -1); 03181 } 03182 } 03183 ao2_unlock(q); 03184 queue_t_unref(q, "Done with iterator"); 03185 if (found) { 03186 break; 03187 } 03188 } 03189 ao2_iterator_destroy(&queue_iter); 03190 return found; 03191 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state, | |||
ptrdiff_t | word_list_offset | |||
) | [static] |
Check if a given word is in a space-delimited list.
line | The line as typed not including the current word being completed | |
word | The word currently being completed | |
pos | The number of completed words in line | |
state | The nth desired completion option | |
word_list_offset | Offset into the line where the list of queues begins. If non-zero, queues in the list will not be offered for further completion. |
Definition at line 7793 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_strdup, queue_t_unref, queues, and word_in_list().
Referenced by complete_queue_add_member(), complete_queue_pause_member(), complete_queue_remove_member(), complete_queue_set_member_penalty(), complete_queue_show(), handle_queue_reload(), and handle_queue_reset().
07794 { 07795 struct call_queue *q; 07796 char *ret = NULL; 07797 int which = 0; 07798 int wordlen = strlen(word); 07799 struct ao2_iterator queue_iter; 07800 const char *word_list = NULL; 07801 07802 /* for certain commands, already completed items should be left out of 07803 * the list */ 07804 if (word_list_offset && strlen(line) >= word_list_offset) { 07805 word_list = line + word_list_offset; 07806 } 07807 07808 queue_iter = ao2_iterator_init(queues, 0); 07809 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07810 if (!strncasecmp(word, q->name, wordlen) && ++which > state 07811 && (!word_list_offset || !word_in_list(word_list, q->name))) { 07812 ret = ast_strdup(q->name); 07813 queue_t_unref(q, "Done with iterator"); 07814 break; 07815 } 07816 queue_t_unref(q, "Done with iterator"); 07817 } 07818 ao2_iterator_destroy(&queue_iter); 07819 07820 /* Pretend "rules" is at the end of the queues list in certain 07821 * circumstances since it is an alternate command that should be 07822 * tab-completable for "queue show" */ 07823 if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) { 07824 ret = ast_strdup("rules"); 07825 } 07826 07827 return ret; 07828 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8256 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
Referenced by handle_queue_add_member().
08257 { 08258 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 08259 switch (pos) { 08260 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 08261 return NULL; 08262 case 4: /* only one possible match, "to" */ 08263 return state == 0 ? ast_strdup("to") : NULL; 08264 case 5: /* <queue> */ 08265 return complete_queue(line, word, pos, state, 0); 08266 case 6: /* only one possible match, "penalty" */ 08267 return state == 0 ? ast_strdup("penalty") : NULL; 08268 case 7: 08269 if (state < 100) { /* 0-99 */ 08270 char *num; 08271 if ((num = ast_malloc(3))) { 08272 sprintf(num, "%d", state); 08273 } 08274 return num; 08275 } else { 08276 return NULL; 08277 } 08278 case 8: /* only one possible match, "as" */ 08279 return state == 0 ? ast_strdup("as") : NULL; 08280 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 08281 return NULL; 08282 default: 08283 return NULL; 08284 } 08285 }
static char* complete_queue_pause_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8478 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_pause_member().
08479 { 08480 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */ 08481 switch (pos) { 08482 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 08483 return NULL; 08484 case 4: /* only one possible match, "queue" */ 08485 return state == 0 ? ast_strdup("queue") : NULL; 08486 case 5: /* <queue> */ 08487 return complete_queue(line, word, pos, state, 0); 08488 case 6: /* "reason" */ 08489 return state == 0 ? ast_strdup("reason") : NULL; 08490 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */ 08491 return NULL; 08492 default: 08493 return NULL; 08494 } 08495 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8386 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().
08387 { 08388 int which = 0; 08389 struct call_queue *q; 08390 struct member *m; 08391 struct ao2_iterator queue_iter; 08392 struct ao2_iterator mem_iter; 08393 int wordlen = strlen(word); 08394 08395 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 08396 if (pos > 5 || pos < 3) 08397 return NULL; 08398 if (pos == 4) /* only one possible match, 'from' */ 08399 return (state == 0 ? ast_strdup("from") : NULL); 08400 08401 if (pos == 5) { /* No need to duplicate code */ 08402 return complete_queue(line, word, pos, state, 0); 08403 } 08404 08405 /* here is the case for 3, <member> */ 08406 queue_iter = ao2_iterator_init(queues, 0); 08407 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 08408 ao2_lock(q); 08409 mem_iter = ao2_iterator_init(q->members, 0); 08410 while ((m = ao2_iterator_next(&mem_iter))) { 08411 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) { 08412 char *tmp; 08413 tmp = ast_strdup(m->interface); 08414 ao2_ref(m, -1); 08415 ao2_iterator_destroy(&mem_iter); 08416 ao2_unlock(q); 08417 queue_t_unref(q, "Done with iterator, returning interface name"); 08418 ao2_iterator_destroy(&queue_iter); 08419 return tmp; 08420 } 08421 ao2_ref(m, -1); 08422 } 08423 ao2_iterator_destroy(&mem_iter); 08424 ao2_unlock(q); 08425 queue_t_unref(q, "Done with iterator"); 08426 } 08427 ao2_iterator_destroy(&queue_iter); 08428 08429 return NULL; 08430 }
static char* complete_queue_rule_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8611 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().
08612 { 08613 int which = 0; 08614 struct rule_list *rl_iter; 08615 int wordlen = strlen(word); 08616 char *ret = NULL; 08617 if (pos != 3) /* Wha? */ { 08618 return NULL; 08619 } 08620 08621 AST_LIST_LOCK(&rule_lists); 08622 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 08623 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) { 08624 ret = ast_strdup(rl_iter->name); 08625 break; 08626 } 08627 } 08628 AST_LIST_UNLOCK(&rule_lists); 08629 08630 return ret; 08631 }
static char* complete_queue_set_member_penalty | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8548 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_set_member_penalty().
08549 { 08550 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/ 08551 switch (pos) { 08552 case 4: 08553 if (state == 0) { 08554 return ast_strdup("on"); 08555 } else { 08556 return NULL; 08557 } 08558 case 6: 08559 if (state == 0) { 08560 return ast_strdup("in"); 08561 } else { 08562 return NULL; 08563 } 08564 case 7: 08565 return complete_queue(line, word, pos, state, 0); 08566 default: 08567 return NULL; 08568 } 08569 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7830 of file app_queue.c.
References complete_queue().
Referenced by queue_show().
07831 { 07832 if (pos == 2) { 07833 return complete_queue(line, word, pos, state, 0); 07834 } 07835 return NULL; 07836 }
static int compress_char | ( | const char | c | ) | [static] |
Definition at line 1829 of file app_queue.c.
Referenced by member_hash_fn().
static void copy_rules | ( | struct queue_ent * | qe, | |
const char * | rulename | |||
) | [static] |
Copy rule from global list into specified queue.
Definition at line 6288 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::parent, and penalty_rule::time.
Referenced by queue_exec().
06289 { 06290 struct penalty_rule *pr_iter; 06291 struct rule_list *rl_iter; 06292 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename; 06293 AST_LIST_LOCK(&rule_lists); 06294 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 06295 if (!strcasecmp(rl_iter->name, tmp)) 06296 break; 06297 } 06298 if (rl_iter) { 06299 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 06300 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr)); 06301 if (!new_pr) { 06302 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n"); 06303 break; 06304 } 06305 new_pr->time = pr_iter->time; 06306 new_pr->max_value = pr_iter->max_value; 06307 new_pr->min_value = pr_iter->min_value; 06308 new_pr->max_relative = pr_iter->max_relative; 06309 new_pr->min_relative = pr_iter->min_relative; 06310 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list); 06311 } 06312 } 06313 AST_LIST_UNLOCK(&rule_lists); 06314 }
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 1797 of file app_queue.c.
References ao2_alloc, ast_copy_string(), ast_log(), ast_strdupa, ast_strlen_zero(), queue_ent::context, exten, get_queue_member_status(), member::interface, LOG_WARNING, member::membername, member::paused, member::penalty, S_OR, member::state_context, member::state_exten, member::state_interface, and member::status.
Referenced by add_to_queue(), reload_single_member(), and rt_handle_member_record().
01798 { 01799 struct member *cur; 01800 01801 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 01802 cur->penalty = penalty; 01803 cur->paused = paused; 01804 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 01805 if (!ast_strlen_zero(state_interface)) 01806 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); 01807 else 01808 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface)); 01809 if (!ast_strlen_zero(membername)) 01810 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 01811 else 01812 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 01813 if (!strchr(cur->interface, '/')) 01814 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 01815 if (!strncmp(cur->state_interface, "hint:", 5)) { 01816 char *tmp = ast_strdupa(cur->state_interface), *context = tmp; 01817 char *exten = strsep(&context, "@") + 5; 01818 01819 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten)); 01820 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context)); 01821 } 01822 cur->status = get_queue_member_status(cur); 01823 } 01824 01825 return cur; 01826 }
static void destroy_queue | ( | void * | obj | ) | [static] |
Free queue's member list then its string fields.
Definition at line 2400 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().
02401 { 02402 struct call_queue *q = obj; 02403 int i; 02404 02405 free_members(q, 1); 02406 ast_string_field_free_memory(q); 02407 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 02408 if (q->sound_periodicannounce[i]) 02409 free(q->sound_periodicannounce[i]); 02410 } 02411 ao2_ref(q->members, -1); 02412 }
static void device_state_cb | ( | const struct ast_event * | event, | |
void * | unused | |||
) | [static] |
Definition at line 1695 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(), and LOG_ERROR.
Referenced by load_module().
01696 { 01697 enum ast_device_state state; 01698 const char *device; 01699 struct statechange *sc; 01700 size_t datapsize; 01701 01702 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); 01703 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); 01704 01705 if (ast_strlen_zero(device)) { 01706 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n"); 01707 return; 01708 } 01709 datapsize = sizeof(*sc) + strlen(device) + 1; 01710 if (!(sc = ast_calloc(1, datapsize))) { 01711 ast_log(LOG_ERROR, "failed to calloc a state change struct\n"); 01712 return; 01713 } 01714 sc->state = state; 01715 strcpy(sc->dev, device); 01716 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) { 01717 ast_free(sc); 01718 } 01719 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 3194 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry(), and wait_for_answer().
03195 { 03196 o->stillgoing = 0; 03197 ast_hangup(o->chan); 03198 o->chan = NULL; 03199 }
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 7575 of file app_queue.c.
References ast_cli(), and astman_append().
Referenced by __queues_show().
07576 { 07577 if (s) 07578 astman_append(s, "%s\r\n", str); 07579 else 07580 ast_cli(fd, "%s\n", str); 07581 }
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 5659 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_free, ast_log(), ast_str_append(), ast_str_buffer(), ast_str_create(), ast_str_strlen(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, member::paused, member::penalty, member::state_interface, and value.
Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().
05660 { 05661 struct member *cur_member; 05662 struct ast_str *value; 05663 struct ao2_iterator mem_iter; 05664 05665 if (!pm_queue) { 05666 return; 05667 } 05668 05669 /* 4K is a reasonable default for most applications, but we grow to 05670 * accommodate more if necessary. */ 05671 if (!(value = ast_str_create(4096))) { 05672 return; 05673 } 05674 05675 mem_iter = ao2_iterator_init(pm_queue->members, 0); 05676 while ((cur_member = ao2_iterator_next(&mem_iter))) { 05677 if (!cur_member->dynamic) { 05678 ao2_ref(cur_member, -1); 05679 continue; 05680 } 05681 05682 ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s", 05683 ast_str_strlen(value) ? "|" : "", 05684 cur_member->interface, 05685 cur_member->penalty, 05686 cur_member->paused, 05687 cur_member->membername, 05688 cur_member->state_interface); 05689 05690 ao2_ref(cur_member, -1); 05691 } 05692 ao2_iterator_destroy(&mem_iter); 05693 05694 if (ast_str_strlen(value) && !cur_member) { 05695 if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) 05696 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 05697 } else { 05698 /* Delete the entry if the queue is empty or there is an error */ 05699 ast_db_del(pm_family, pm_queue->name); 05700 } 05701 05702 ast_free(value); 05703 }
static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 4779 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, queue_ent::chan, queue_end_bridge::q, queue_t_unref, and set_queue_variables().
Referenced by try_calling().
04780 { 04781 struct queue_end_bridge *qeb = data; 04782 struct call_queue *q = qeb->q; 04783 struct ast_channel *chan = qeb->chan; 04784 04785 if (ao2_ref(qeb, -1) == 1) { 04786 set_queue_variables(q, chan); 04787 /* This unrefs the reference we made in try_calling when we allocated qeb */ 04788 queue_t_unref(q, "Expire bridge_config reference"); 04789 } 04790 }
static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, | |
struct ast_channel * | originator, | |||
struct ast_channel * | terminator | |||
) | [static] |
Definition at line 4772 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, and ast_bridge_config::end_bridge_callback_data.
Referenced by try_calling().
04773 { 04774 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data; 04775 ao2_ref(qeb, +1); 04776 qeb->chan = originator; 04777 }
static int extension_state_cb | ( | char * | context, | |
char * | exten, | |||
enum ast_extension_states | state, | |||
void * | data | |||
) | [static] |
Definition at line 1753 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_debug, ast_devstate2str(), extensionstate2devicestate(), call_queue::found, call_queue::members, queue_t_unref, queues, member::state_context, member::state_exten, and update_status().
Referenced by load_module(), and unload_module().
01754 { 01755 struct ao2_iterator miter, qiter; 01756 struct member *m; 01757 struct call_queue *q; 01758 int found = 0, device_state = extensionstate2devicestate(state); 01759 01760 qiter = ao2_iterator_init(queues, 0); 01761 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) { 01762 ao2_lock(q); 01763 01764 miter = ao2_iterator_init(q->members, 0); 01765 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01766 if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) { 01767 update_status(q, m, device_state); 01768 ao2_ref(m, -1); 01769 found = 1; 01770 break; 01771 } 01772 } 01773 ao2_iterator_destroy(&miter); 01774 01775 ao2_unlock(q); 01776 queue_t_unref(q, "Done with iterator"); 01777 } 01778 ao2_iterator_destroy(&qiter); 01779 01780 if (found) { 01781 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state)); 01782 } else { 01783 ast_debug(3, "Extension '%s@%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", 01784 exten, context, device_state, ast_devstate2str(device_state)); 01785 } 01786 01787 return 0; 01788 }
static int extensionstate2devicestate | ( | int | state | ) | [static] |
Helper function which converts from extension state to device state values.
Definition at line 1722 of file app_queue.c.
References AST_DEVICE_BUSY, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_UNAVAILABLE, AST_EXTENSION_BUSY, AST_EXTENSION_DEACTIVATED, AST_EXTENSION_INUSE, AST_EXTENSION_NOT_INUSE, AST_EXTENSION_ONHOLD, AST_EXTENSION_REMOVED, AST_EXTENSION_RINGING, and AST_EXTENSION_UNAVAILABLE.
Referenced by extension_state_cb(), and get_queue_member_status().
01723 { 01724 switch (state) { 01725 case AST_EXTENSION_NOT_INUSE: 01726 state = AST_DEVICE_NOT_INUSE; 01727 break; 01728 case AST_EXTENSION_INUSE: 01729 state = AST_DEVICE_INUSE; 01730 break; 01731 case AST_EXTENSION_BUSY: 01732 state = AST_DEVICE_BUSY; 01733 break; 01734 case AST_EXTENSION_RINGING: 01735 state = AST_DEVICE_RINGING; 01736 break; 01737 case AST_EXTENSION_ONHOLD: 01738 state = AST_DEVICE_ONHOLD; 01739 break; 01740 case AST_EXTENSION_UNAVAILABLE: 01741 state = AST_DEVICE_UNAVAILABLE; 01742 break; 01743 case AST_EXTENSION_REMOVED: 01744 case AST_EXTENSION_DEACTIVATED: 01745 default: 01746 state = AST_DEVICE_INVALID; 01747 break; 01748 } 01749 01750 return state; 01751 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static, read] |
find the entry with the best metric, or NULL
Definition at line 3536 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
Referenced by ring_one(), store_next_lin(), and store_next_rr().
03537 { 03538 struct callattempt *best = NULL, *cur; 03539 03540 for (cur = outgoing; cur; cur = cur->q_next) { 03541 if (cur->stillgoing && /* Not already done */ 03542 !cur->chan && /* Isn't already going */ 03543 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 03544 best = cur; 03545 } 03546 } 03547 03548 return best; 03549 }
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.
the | queue, | |
NULL | if it doesn't exist. |
Definition at line 2438 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_unlock, ast_category_browse(), ast_copy_string(), ast_debug, ast_log(), ast_queue_log(), ast_variable_retrieve(), clear_queue(), member::dead, call_queue::dead, init_queue(), member::interface, LOG_WARNING, member_remove_from_queue(), call_queue::members, ast_variable::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, rt_handle_member_record(), S_OR, strat2int(), call_queue::strategy, and ast_variable::value.
Referenced by load_realtime_queue().
02439 { 02440 struct ast_variable *v; 02441 struct call_queue *q, tmpq = { 02442 .name = queuename, 02443 }; 02444 struct member *m; 02445 struct ao2_iterator mem_iter; 02446 char *interface = NULL; 02447 const char *tmp_name; 02448 char *tmp; 02449 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 02450 02451 /* Static queues override realtime. */ 02452 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) { 02453 ao2_lock(q); 02454 if (!q->realtime) { 02455 if (q->dead) { 02456 ao2_unlock(q); 02457 queue_t_unref(q, "Queue is dead; can't return it"); 02458 return NULL; 02459 } else { 02460 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 02461 ao2_unlock(q); 02462 return q; 02463 } 02464 } 02465 } else if (!member_config) 02466 /* Not found in the list, and it's not realtime ... */ 02467 return NULL; 02468 02469 /* Check if queue is defined in realtime. */ 02470 if (!queue_vars) { 02471 /* Delete queue from in-core list if it has been deleted in realtime. */ 02472 if (q) { 02473 /*! \note Hmm, can't seem to distinguish a DB failure from a not 02474 found condition... So we might delete an in-core queue 02475 in case of DB failure. */ 02476 ast_debug(1, "Queue %s not found in realtime.\n", queuename); 02477 02478 q->dead = 1; 02479 /* Delete if unused (else will be deleted when last caller leaves). */ 02480 queues_t_unlink(queues, q, "Unused; removing from container"); 02481 ao2_unlock(q); 02482 queue_t_unref(q, "Queue is dead; can't return it"); 02483 } 02484 return NULL; 02485 } 02486 02487 /* Create a new queue if an in-core entry does not exist yet. */ 02488 if (!q) { 02489 struct ast_variable *tmpvar = NULL; 02490 if (!(q = alloc_queue(queuename))) 02491 return NULL; 02492 ao2_lock(q); 02493 clear_queue(q); 02494 q->realtime = 1; 02495 /*Before we initialize the queue, we need to set the strategy, so that linear strategy 02496 * will allocate the members properly 02497 */ 02498 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { 02499 if (!strcasecmp(tmpvar->name, "strategy")) { 02500 q->strategy = strat2int(tmpvar->value); 02501 if (q->strategy < 0) { 02502 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 02503 tmpvar->value, q->name); 02504 q->strategy = QUEUE_STRATEGY_RINGALL; 02505 } 02506 break; 02507 } 02508 } 02509 /* We traversed all variables and didn't find a strategy */ 02510 if (!tmpvar) 02511 q->strategy = QUEUE_STRATEGY_RINGALL; 02512 queues_t_link(queues, q, "Add queue to container"); 02513 } 02514 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 02515 02516 memset(tmpbuf, 0, sizeof(tmpbuf)); 02517 for (v = queue_vars; v; v = v->next) { 02518 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 02519 if (strchr(v->name, '_')) { 02520 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 02521 tmp_name = tmpbuf; 02522 tmp = tmpbuf; 02523 while ((tmp = strchr(tmp, '_'))) 02524 *tmp++ = '-'; 02525 } else 02526 tmp_name = v->name; 02527 02528 /* NULL values don't get returned from realtime; blank values should 02529 * still get set. If someone doesn't want a value to be set, they 02530 * should set the realtime column to NULL, not blank. */ 02531 queue_set_param(q, tmp_name, v->value, -1, 0); 02532 } 02533 02534 /* Temporarily set realtime members dead so we can detect deleted ones. */ 02535 mem_iter = ao2_iterator_init(q->members, 0); 02536 while ((m = ao2_iterator_next(&mem_iter))) { 02537 if (m->realtime) 02538 m->dead = 1; 02539 ao2_ref(m, -1); 02540 } 02541 ao2_iterator_destroy(&mem_iter); 02542 02543 while ((interface = ast_category_browse(member_config, interface))) { 02544 rt_handle_member_record(q, interface, 02545 ast_variable_retrieve(member_config, interface, "uniqueid"), 02546 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface), 02547 ast_variable_retrieve(member_config, interface, "penalty"), 02548 ast_variable_retrieve(member_config, interface, "paused"), 02549 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface)); 02550 } 02551 02552 /* Delete all realtime members that have been deleted in DB. */ 02553 mem_iter = ao2_iterator_init(q->members, 0); 02554 while ((m = ao2_iterator_next(&mem_iter))) { 02555 if (m->dead) { 02556 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 02557 member_remove_from_queue(q, m); 02558 } 02559 ao2_ref(m, -1); 02560 } 02561 ao2_iterator_destroy(&mem_iter); 02562 02563 ao2_unlock(q); 02564 02565 return q; 02566 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Iterate through queue's member list and delete them.
Definition at line 2384 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, member::dynamic, member_remove_from_queue(), and call_queue::members.
Referenced by destroy_queue().
02385 { 02386 /* Free non-dynamic members */ 02387 struct member *cur; 02388 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 02389 02390 while ((cur = ao2_iterator_next(&mem_iter))) { 02391 if (all || !cur->dynamic) { 02392 member_remove_from_queue(q, cur); 02393 } 02394 ao2_ref(cur, -1); 02395 } 02396 ao2_iterator_destroy(&mem_iter); 02397 }
static int get_member_penalty | ( | char * | queuename, | |
char * | interface | |||
) | [static] |
Definition at line 5936 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().
05937 { 05938 int foundqueue = 0, penalty; 05939 struct call_queue *q, tmpq = { 05940 .name = queuename, 05941 }; 05942 struct member *mem; 05943 05944 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) { 05945 foundqueue = 1; 05946 ao2_lock(q); 05947 if ((mem = interface_exists(q, interface))) { 05948 penalty = mem->penalty; 05949 ao2_ref(mem, -1); 05950 ao2_unlock(q); 05951 queue_t_unref(q, "Search complete"); 05952 return penalty; 05953 } 05954 ao2_unlock(q); 05955 queue_t_unref(q, "Search complete"); 05956 } 05957 05958 /* some useful debuging */ 05959 if (foundqueue) 05960 ast_log (LOG_ERROR, "Invalid queuename\n"); 05961 else 05962 ast_log (LOG_ERROR, "Invalid interface\n"); 05963 05964 return RESULT_FAILURE; 05965 }
static int get_member_status | ( | struct call_queue * | q, | |
int | max_penalty, | |||
int | min_penalty, | |||
enum empty_conditions | conditions, | |||
int | devstate | |||
) | [static] |
Check if members are available.
This function checks to see if members are available to be called. If any member is available, the function immediately returns 0. If no members are available, then -1 is returned.
Definition at line 1541 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_RINGING, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, member::lastcall, member::membername, call_queue::members, member::paused, member::penalty, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_RINGING, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, QUEUE_EMPTY_WRAPUP, member::state_interface, member::status, and call_queue::wrapuptime.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
01542 { 01543 struct member *member; 01544 struct ao2_iterator mem_iter; 01545 01546 ao2_lock(q); 01547 mem_iter = ao2_iterator_init(q->members, 0); 01548 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) { 01549 if ((max_penalty != INT_MAX && member->penalty > max_penalty) || (min_penalty != INT_MAX && member->penalty < min_penalty)) { 01550 if (conditions & QUEUE_EMPTY_PENALTY) { 01551 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty); 01552 continue; 01553 } 01554 } 01555 01556 switch (devstate ? ast_device_state(member->state_interface) : member->status) { 01557 case AST_DEVICE_INVALID: 01558 if (conditions & QUEUE_EMPTY_INVALID) { 01559 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername); 01560 break; 01561 } 01562 goto default_case; 01563 case AST_DEVICE_UNAVAILABLE: 01564 if (conditions & QUEUE_EMPTY_UNAVAILABLE) { 01565 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername); 01566 break; 01567 } 01568 goto default_case; 01569 case AST_DEVICE_INUSE: 01570 if (conditions & QUEUE_EMPTY_INUSE) { 01571 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername); 01572 break; 01573 } 01574 goto default_case; 01575 case AST_DEVICE_RINGING: 01576 if (conditions & QUEUE_EMPTY_RINGING) { 01577 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername); 01578 break; 01579 } 01580 goto default_case; 01581 case AST_DEVICE_UNKNOWN: 01582 if (conditions & QUEUE_EMPTY_UNKNOWN) { 01583 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername); 01584 break; 01585 } 01586 /* Fall-through */ 01587 default: 01588 default_case: 01589 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) { 01590 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername); 01591 break; 01592 } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) { 01593 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime); 01594 break; 01595 } else { 01596 ao2_ref(member, -1); 01597 ao2_iterator_destroy(&mem_iter); 01598 ao2_unlock(q); 01599 ast_debug(4, "%s is available.\n", member->membername); 01600 return 0; 01601 } 01602 break; 01603 } 01604 } 01605 ao2_iterator_destroy(&mem_iter); 01606 ao2_unlock(q); 01607 01608 if (!devstate && (conditions & QUEUE_EMPTY_RINGING)) { 01609 /* member state still may be RINGING due to lag in event message - check again with device state */ 01610 return get_member_status(q, max_penalty, min_penalty, conditions, 1); 01611 } 01612 return -1; 01613 }
static int get_queue_member_status | ( | struct member * | cur | ) | [static] |
Return the current state of a member.
Definition at line 1791 of file app_queue.c.
References ast_extension_state(), ast_strlen_zero(), extensionstate2devicestate(), member::state_context, member::state_exten, and member::state_interface.
Referenced by can_ring_entry(), create_queue_member(), and kill_dead_members().
01792 { 01793 return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten)); 01794 }
static char* handle_queue_add_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8312 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.
08313 { 08314 const char *queuename, *interface, *membername = NULL, *state_interface = NULL; 08315 int penalty; 08316 08317 switch ( cmd ) { 08318 case CLI_INIT: 08319 e->command = "queue add member"; 08320 e->usage = 08321 "Usage: queue add member <dial string> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n" 08322 " Add a dial string (Such as a channel,e.g. SIP/6001) to a queue with optionally: a penalty, membername and a state_interface\n"; 08323 return NULL; 08324 case CLI_GENERATE: 08325 return complete_queue_add_member(a->line, a->word, a->pos, a->n); 08326 } 08327 08328 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) { 08329 return CLI_SHOWUSAGE; 08330 } else if (strcmp(a->argv[4], "to")) { 08331 return CLI_SHOWUSAGE; 08332 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) { 08333 return CLI_SHOWUSAGE; 08334 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) { 08335 return CLI_SHOWUSAGE; 08336 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) { 08337 return CLI_SHOWUSAGE; 08338 } 08339 08340 queuename = a->argv[5]; 08341 interface = a->argv[3]; 08342 if (a->argc >= 8) { 08343 if (sscanf(a->argv[7], "%30d", &penalty) == 1) { 08344 if (penalty < 0) { 08345 ast_cli(a->fd, "Penalty must be >= 0\n"); 08346 penalty = 0; 08347 } 08348 } else { 08349 ast_cli(a->fd, "Penalty must be an integer >= 0\n"); 08350 penalty = 0; 08351 } 08352 } else { 08353 penalty = 0; 08354 } 08355 08356 if (a->argc >= 10) { 08357 membername = a->argv[9]; 08358 } 08359 08360 if (a->argc >= 12) { 08361 state_interface = a->argv[11]; 08362 } 08363 08364 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) { 08365 case RES_OKAY: 08366 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 08367 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 08368 return CLI_SUCCESS; 08369 case RES_EXISTS: 08370 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 08371 return CLI_FAILURE; 08372 case RES_NOSUCHQUEUE: 08373 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 08374 return CLI_FAILURE; 08375 case RES_OUTOFMEMORY: 08376 ast_cli(a->fd, "Out of memory\n"); 08377 return CLI_FAILURE; 08378 case RES_NOT_DYNAMIC: 08379 ast_cli(a->fd, "Member not dynamic\n"); 08380 return CLI_FAILURE; 08381 default: 08382 return CLI_FAILURE; 08383 } 08384 }
static char* handle_queue_pause_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8497 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_pause_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, RESULT_SUCCESS, set_member_paused(), ast_cli_entry::usage, and word.
08498 { 08499 const char *queuename, *interface, *reason; 08500 int paused; 08501 08502 switch (cmd) { 08503 case CLI_INIT: 08504 e->command = "queue {pause|unpause} member"; 08505 e->usage = 08506 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n" 08507 " Pause or unpause a queue member. Not specifying a particular queue\n" 08508 " will pause or unpause a member across all queues to which the member\n" 08509 " belongs.\n"; 08510 return NULL; 08511 case CLI_GENERATE: 08512 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n); 08513 } 08514 08515 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) { 08516 return CLI_SHOWUSAGE; 08517 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) { 08518 return CLI_SHOWUSAGE; 08519 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) { 08520 return CLI_SHOWUSAGE; 08521 } 08522 08523 08524 interface = a->argv[3]; 08525 queuename = a->argc >= 6 ? a->argv[5] : NULL; 08526 reason = a->argc == 8 ? a->argv[7] : NULL; 08527 paused = !strcasecmp(a->argv[1], "pause"); 08528 08529 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) { 08530 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface); 08531 if (!ast_strlen_zero(queuename)) 08532 ast_cli(a->fd, " in queue '%s'", queuename); 08533 if (!ast_strlen_zero(reason)) 08534 ast_cli(a->fd, " for reason '%s'", reason); 08535 ast_cli(a->fd, "\n"); 08536 return CLI_SUCCESS; 08537 } else { 08538 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface); 08539 if (!ast_strlen_zero(queuename)) 08540 ast_cli(a->fd, " in queue '%s'", queuename); 08541 if (!ast_strlen_zero(reason)) 08542 ast_cli(a->fd, " for reason '%s'", reason); 08543 ast_cli(a->fd, "\n"); 08544 return CLI_FAILURE; 08545 } 08546 }
static char* handle_queue_reload | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8706 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, AST_FLAGS_ALL, ast_set_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue(), ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, reload_handler(), ast_cli_entry::usage, and ast_cli_args::word.
08707 { 08708 struct ast_flags mask = {0,}; 08709 int i; 08710 08711 switch (cmd) { 08712 case CLI_INIT: 08713 e->command = "queue reload {parameters|members|rules|all}"; 08714 e->usage = 08715 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n" 08716 "Reload queues. If <queuenames> are specified, only reload information pertaining\n" 08717 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n" 08718 "specified in order to know what information to reload. Below is an explanation\n" 08719 "of each of these qualifiers.\n" 08720 "\n" 08721 "\t'members' - reload queue members from queues.conf\n" 08722 "\t'parameters' - reload all queue options except for queue members\n" 08723 "\t'rules' - reload the queuerules.conf file\n" 08724 "\t'all' - reload queue rules, parameters, and members\n" 08725 "\n" 08726 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n" 08727 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n" 08728 "one queue is specified when using this command, reloading queue rules may cause\n" 08729 "other queues to be affected\n"; 08730 return NULL; 08731 case CLI_GENERATE: 08732 if (a->pos >= 3) { 08733 /* find the point at which the list of queue names starts */ 08734 const char *command_end = a->line + strlen("queue reload "); 08735 command_end = strchr(command_end, ' '); 08736 if (!command_end) { 08737 command_end = a->line + strlen(a->line); 08738 } 08739 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line); 08740 } else { 08741 return NULL; 08742 } 08743 } 08744 08745 if (a->argc < 3) 08746 return CLI_SHOWUSAGE; 08747 08748 if (!strcasecmp(a->argv[2], "rules")) { 08749 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 08750 } else if (!strcasecmp(a->argv[2], "members")) { 08751 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 08752 } else if (!strcasecmp(a->argv[2], "parameters")) { 08753 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 08754 } else if (!strcasecmp(a->argv[2], "all")) { 08755 ast_set_flag(&mask, AST_FLAGS_ALL); 08756 } 08757 08758 if (a->argc == 3) { 08759 reload_handler(1, &mask, NULL); 08760 return CLI_SUCCESS; 08761 } 08762 08763 for (i = 3; i < a->argc; ++i) { 08764 reload_handler(1, &mask, a->argv[i]); 08765 } 08766 08767 return CLI_SUCCESS; 08768 }
static char* handle_queue_remove_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8432 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_remove_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.
08433 { 08434 const char *queuename, *interface; 08435 08436 switch (cmd) { 08437 case CLI_INIT: 08438 e->command = "queue remove member"; 08439 e->usage = 08440 "Usage: queue remove member <channel> from <queue>\n" 08441 " Remove a specific channel from a queue.\n"; 08442 return NULL; 08443 case CLI_GENERATE: 08444 return complete_queue_remove_member(a->line, a->word, a->pos, a->n); 08445 } 08446 08447 if (a->argc != 6) { 08448 return CLI_SHOWUSAGE; 08449 } else if (strcmp(a->argv[4], "from")) { 08450 return CLI_SHOWUSAGE; 08451 } 08452 08453 queuename = a->argv[5]; 08454 interface = a->argv[3]; 08455 08456 switch (remove_from_queue(queuename, interface)) { 08457 case RES_OKAY: 08458 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 08459 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 08460 return CLI_SUCCESS; 08461 case RES_EXISTS: 08462 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 08463 return CLI_FAILURE; 08464 case RES_NOSUCHQUEUE: 08465 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 08466 return CLI_FAILURE; 08467 case RES_OUTOFMEMORY: 08468 ast_cli(a->fd, "Out of memory\n"); 08469 return CLI_FAILURE; 08470 case RES_NOT_DYNAMIC: 08471 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename); 08472 return CLI_FAILURE; 08473 default: 08474 return CLI_FAILURE; 08475 } 08476 }
static char* handle_queue_reset | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8667 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue(), ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, QUEUE_RESET_STATS, reload_handler(), ast_cli_entry::usage, and ast_cli_args::word.
08668 { 08669 struct ast_flags mask = {QUEUE_RESET_STATS,}; 08670 int i; 08671 08672 switch (cmd) { 08673 case CLI_INIT: 08674 e->command = "queue reset stats"; 08675 e->usage = 08676 "Usage: queue reset stats [<queuenames>]\n" 08677 "\n" 08678 "Issuing this command will reset statistics for\n" 08679 "<queuenames>, or for all queues if no queue is\n" 08680 "specified.\n"; 08681 return NULL; 08682 case CLI_GENERATE: 08683 if (a->pos >= 3) { 08684 return complete_queue(a->line, a->word, a->pos, a->n, 17); 08685 } else { 08686 return NULL; 08687 } 08688 } 08689 08690 if (a->argc < 3) { 08691 return CLI_SHOWUSAGE; 08692 } 08693 08694 if (a->argc == 3) { 08695 reload_handler(1, &mask, NULL); 08696 return CLI_SUCCESS; 08697 } 08698 08699 for (i = 3; i < a->argc; ++i) { 08700 reload_handler(1, &mask, a->argv[i]); 08701 } 08702 08703 return CLI_SUCCESS; 08704 }
static char* handle_queue_rule_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8633 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, penalty_rule::time, ast_cli_entry::usage, and ast_cli_args::word.
08634 { 08635 const char *rule; 08636 struct rule_list *rl_iter; 08637 struct penalty_rule *pr_iter; 08638 switch (cmd) { 08639 case CLI_INIT: 08640 e->command = "queue show rules"; 08641 e->usage = 08642 "Usage: queue show rules [rulename]\n" 08643 " Show the list of rules associated with rulename. If no\n" 08644 " rulename is specified, list all rules defined in queuerules.conf\n"; 08645 return NULL; 08646 case CLI_GENERATE: 08647 return complete_queue_rule_show(a->line, a->word, a->pos, a->n); 08648 } 08649 08650 if (a->argc != 3 && a->argc != 4) 08651 return CLI_SHOWUSAGE; 08652 08653 rule = a->argc == 4 ? a->argv[3] : ""; 08654 AST_LIST_LOCK(&rule_lists); 08655 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 08656 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) { 08657 ast_cli(a->fd, "Rule: %s\n", rl_iter->name); 08658 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 08659 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); 08660 } 08661 } 08662 } 08663 AST_LIST_UNLOCK(&rule_lists); 08664 return CLI_SUCCESS; 08665 }
static char* handle_queue_set_member_penalty | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8571 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.
08572 { 08573 const char *queuename = NULL, *interface; 08574 int penalty = 0; 08575 08576 switch (cmd) { 08577 case CLI_INIT: 08578 e->command = "queue set penalty"; 08579 e->usage = 08580 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n" 08581 " Set a member's penalty in the queue specified. If no queue is specified\n" 08582 " then that interface's penalty is set in all queues to which that interface is a member\n"; 08583 return NULL; 08584 case CLI_GENERATE: 08585 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n); 08586 } 08587 08588 if (a->argc != 6 && a->argc != 8) { 08589 return CLI_SHOWUSAGE; 08590 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) { 08591 return CLI_SHOWUSAGE; 08592 } 08593 08594 if (a->argc == 8) 08595 queuename = a->argv[7]; 08596 interface = a->argv[5]; 08597 penalty = atoi(a->argv[3]); 08598 08599 switch (set_member_penalty(queuename, interface, penalty)) { 08600 case RESULT_SUCCESS: 08601 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); 08602 return CLI_SUCCESS; 08603 case RESULT_FAILURE: 08604 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); 08605 return CLI_FAILURE; 08606 default: 08607 return CLI_FAILURE; 08608 } 08609 }
static int handle_statechange | ( | void * | datap | ) | [static] |
set a member's status based on device state of that member's interface
Definition at line 1651 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_copy_string(), ast_debug, ast_devstate2str(), ast_free, statechange::dev, call_queue::found, call_queue::members, queue_t_unref, queues, member::state_interface, and update_status().
Referenced by device_state_cb().
01652 { 01653 struct statechange *sc = datap; 01654 struct ao2_iterator miter, qiter; 01655 struct member *m; 01656 struct call_queue *q; 01657 char interface[80], *slash_pos; 01658 int found = 0; 01659 01660 qiter = ao2_iterator_init(queues, 0); 01661 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) { 01662 ao2_lock(q); 01663 01664 miter = ao2_iterator_init(q->members, 0); 01665 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01666 ast_copy_string(interface, m->state_interface, sizeof(interface)); 01667 01668 if ((slash_pos = strchr(interface, '/'))) 01669 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) 01670 *slash_pos = '\0'; 01671 01672 if (!strcasecmp(interface, sc->dev)) { 01673 found = 1; 01674 update_status(q, m, sc->state); 01675 ao2_ref(m, -1); 01676 break; 01677 } 01678 } 01679 ao2_iterator_destroy(&miter); 01680 01681 ao2_unlock(q); 01682 queue_t_unref(q, "Done with iterator"); 01683 } 01684 ao2_iterator_destroy(&qiter); 01685 01686 if (found) 01687 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state)); 01688 else 01689 ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, ast_devstate2str(sc->state)); 01690 01691 ast_free(sc); 01692 return 0; 01693 }
static void hangupcalls | ( | struct callattempt * | outgoing, | |
struct ast_channel * | exception, | |||
int | cancel_answered_elsewhere | |||
) | [static] |
Hang up a list of outgoing calls.
Definition at line 3086 of file app_queue.c.
References callattempt::aoc_s_rate_list, ast_aoc_destroy_decoded(), AST_FLAG_ANSWERED_ELSEWHERE, ast_hangup(), ast_set_flag, callattempt_free(), callattempt::chan, and callattempt::q_next.
Referenced by try_calling().
03087 { 03088 struct callattempt *oo; 03089 03090 while (outgoing) { 03091 /* If someone else answered the call we should indicate this in the CANCEL */ 03092 /* Hangup any existing lines we have open */ 03093 if (outgoing->chan && (outgoing->chan != exception)) { 03094 if (exception || cancel_answered_elsewhere) 03095 ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE); 03096 ast_hangup(outgoing->chan); 03097 } 03098 oo = outgoing; 03099 outgoing = outgoing->q_next; 03100 ast_aoc_destroy_decoded(oo->aoc_s_rate_list); 03101 callattempt_free(oo); 03102 } 03103 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Initialize Queue default values.
Definition at line 1861 of file app_queue.c.
References call_queue::announce_to_first_user, call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::autopause, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, call_queue::penaltymemberslimit, call_queue::periodicannouncefrequency, QUEUE_AUTOPAUSE_OFF, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRORDERED, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, 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_single_queue().
01862 { 01863 int i; 01864 struct penalty_rule *pr_iter; 01865 01866 q->dead = 0; 01867 q->retry = DEFAULT_RETRY; 01868 q->timeout = DEFAULT_TIMEOUT; 01869 q->maxlen = 0; 01870 q->ringlimit = 0; 01871 q->announcefrequency = 0; 01872 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY; 01873 q->announceholdtime = 1; 01874 q->announcepositionlimit = 10; /* Default 10 positions */ 01875 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */ 01876 q->roundingseconds = 0; /* Default - don't announce seconds */ 01877 q->servicelevel = 0; 01878 q->ringinuse = 1; 01879 q->announce_to_first_user = 0; 01880 q->setinterfacevar = 0; 01881 q->setqueuevar = 0; 01882 q->setqueueentryvar = 0; 01883 q->autofill = autofill_default; 01884 q->montype = montype_default; 01885 q->monfmt[0] = '\0'; 01886 q->reportholdtime = 0; 01887 q->wrapuptime = 0; 01888 q->penaltymemberslimit = 0; 01889 q->joinempty = 0; 01890 q->leavewhenempty = 0; 01891 q->memberdelay = 0; 01892 q->maskmemberstatus = 0; 01893 q->eventwhencalled = 0; 01894 q->weight = 0; 01895 q->timeoutrestart = 0; 01896 q->periodicannouncefrequency = 0; 01897 q->randomperiodicannounce = 0; 01898 q->numperiodicannounce = 0; 01899 q->autopause = QUEUE_AUTOPAUSE_OFF; 01900 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01901 if (!q->members) { 01902 if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED) 01903 /* linear strategy depends on order, so we have to place all members in a single bucket */ 01904 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); 01905 else 01906 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 01907 } 01908 q->found = 1; 01909 01910 ast_string_field_set(q, sound_next, "queue-youarenext"); 01911 ast_string_field_set(q, sound_thereare, "queue-thereare"); 01912 ast_string_field_set(q, sound_calls, "queue-callswaiting"); 01913 ast_string_field_set(q, queue_quantity1, "queue-quantity1"); 01914 ast_string_field_set(q, queue_quantity2, "queue-quantity2"); 01915 ast_string_field_set(q, sound_holdtime, "queue-holdtime"); 01916 ast_string_field_set(q, sound_minutes, "queue-minutes"); 01917 ast_string_field_set(q, sound_minute, "queue-minute"); 01918 ast_string_field_set(q, sound_seconds, "queue-seconds"); 01919 ast_string_field_set(q, sound_thanks, "queue-thankyou"); 01920 ast_string_field_set(q, sound_reporthold, "queue-reporthold"); 01921 01922 if (!q->sound_periodicannounce[0]) { 01923 q->sound_periodicannounce[0] = ast_str_create(32); 01924 } 01925 01926 if (q->sound_periodicannounce[0]) { 01927 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce"); 01928 } 01929 01930 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 01931 if (q->sound_periodicannounce[i]) 01932 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", ""); 01933 } 01934 01935 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) 01936 ast_free(pr_iter); 01937 }
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 1511 of file app_queue.c.
References call_queue::head, and queue_ref().
Referenced by join_queue().
01512 { 01513 struct queue_ent *cur; 01514 01515 if (!q || !new) 01516 return; 01517 if (prev) { 01518 cur = prev->next; 01519 prev->next = new; 01520 } else { 01521 cur = q->head; 01522 q->head = new; 01523 } 01524 new->next = cur; 01525 01526 /* every queue_ent must have a reference to it's parent call_queue, this 01527 * reference does not go away until the end of the queue_ent's life, meaning 01528 * that even when the queue_ent leaves the call_queue this ref must remain. */ 01529 queue_ref(q); 01530 new->parent = q; 01531 new->pos = ++(*pos); 01532 new->opos = *pos; 01533 }
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.
-1 | on failure | |
0 | on success |
Definition at line 1968 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_WARNING, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, and penalty_rule::time.
Referenced by reload_queue_rules().
01969 { 01970 char *timestr, *maxstr, *minstr, *contentdup; 01971 struct penalty_rule *rule = NULL, *rule_iter; 01972 struct rule_list *rl_iter; 01973 int penaltychangetime, inserted = 0; 01974 01975 if (!(rule = ast_calloc(1, sizeof(*rule)))) { 01976 return -1; 01977 } 01978 01979 contentdup = ast_strdupa(content); 01980 01981 if (!(maxstr = strchr(contentdup, ','))) { 01982 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum); 01983 ast_free(rule); 01984 return -1; 01985 } 01986 01987 *maxstr++ = '\0'; 01988 timestr = contentdup; 01989 01990 if ((penaltychangetime = atoi(timestr)) < 0) { 01991 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum); 01992 ast_free(rule); 01993 return -1; 01994 } 01995 01996 rule->time = penaltychangetime; 01997 01998 if ((minstr = strchr(maxstr,','))) 01999 *minstr++ = '\0'; 02000 02001 /* The last check will evaluate true if either no penalty change is indicated for a given rule 02002 * OR if a min penalty change is indicated but no max penalty change is */ 02003 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') { 02004 rule->max_relative = 1; 02005 } 02006 02007 rule->max_value = atoi(maxstr); 02008 02009 if (!ast_strlen_zero(minstr)) { 02010 if (*minstr == '+' || *minstr == '-') 02011 rule->min_relative = 1; 02012 rule->min_value = atoi(minstr); 02013 } else /*there was no minimum specified, so assume this means no change*/ 02014 rule->min_relative = 1; 02015 02016 /*We have the rule made, now we need to insert it where it belongs*/ 02017 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){ 02018 if (strcasecmp(rl_iter->name, list_name)) 02019 continue; 02020 02021 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) { 02022 if (rule->time < rule_iter->time) { 02023 AST_LIST_INSERT_BEFORE_CURRENT(rule, list); 02024 inserted = 1; 02025 break; 02026 } 02027 } 02028 AST_LIST_TRAVERSE_SAFE_END; 02029 02030 if (!inserted) { 02031 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list); 02032 inserted = 1; 02033 } 02034 02035 break; 02036 } 02037 02038 if (!inserted) { 02039 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name); 02040 ast_free(rule); 02041 return -1; 02042 } 02043 return 0; 02044 }
static const char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 1349 of file app_queue.c.
References ARRAY_LEN, strategy::name, and strategies.
Referenced by __queues_show(), manager_queues_status(), queue_function_var(), queues_data_provider_get_helper(), and set_queue_variables().
01350 { 01351 int x; 01352 01353 for (x = 0; x < ARRAY_LEN(strategies); x++) { 01354 if (strategy == strategies[x].strategy) 01355 return strategies[x].name; 01356 } 01357 01358 return "<unknown>"; 01359 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static, read] |
Definition at line 5633 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().
05634 { 05635 struct member *mem; 05636 struct ao2_iterator mem_iter; 05637 05638 if (!q) 05639 return NULL; 05640 05641 mem_iter = ao2_iterator_init(q->members, 0); 05642 while ((mem = ao2_iterator_next(&mem_iter))) { 05643 if (!strcasecmp(interface, mem->interface)) { 05644 ao2_iterator_destroy(&mem_iter); 05645 return mem; 05646 } 05647 ao2_ref(mem, -1); 05648 } 05649 ao2_iterator_destroy(&mem_iter); 05650 05651 return NULL; 05652 }
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.
[in] | qe | The caller who wants to know if it is his turn |
0 | It is not our turn | |
1 | It is our turn |
Definition at line 4320 of file app_queue.c.
References ao2_lock, ao2_unlock, ast_debug, call_queue::autofill, queue_ent::chan, call_queue::head, num_available_members(), queue_ent::parent, queue_ent::pending, and queue_ent::pos.
Referenced by queue_exec(), and wait_our_turn().
04321 { 04322 struct queue_ent *ch; 04323 int res; 04324 int avl; 04325 int idx = 0; 04326 /* This needs a lock. How many members are available to be served? */ 04327 ao2_lock(qe->parent); 04328 04329 avl = num_available_members(qe->parent); 04330 04331 ch = qe->parent->head; 04332 04333 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); 04334 04335 while ((idx < avl) && (ch) && (ch != qe)) { 04336 if (!ch->pending) 04337 idx++; 04338 ch = ch->next; 04339 } 04340 04341 ao2_unlock(qe->parent); 04342 /* If the queue entry is within avl [the number of available members] calls from the top ... 04343 * Autofill and position check added to support autofill=no (as only calls 04344 * from the front of the queue are valid when autofill is disabled) 04345 */ 04346 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) { 04347 ast_debug(1, "It's our turn (%s).\n", qe->chan->name); 04348 res = 1; 04349 } else { 04350 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); 04351 res = 0; 04352 } 04353 04354 return res; 04355 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason, | |||
int | position | |||
) | [static] |
Definition at line 2696 of file app_queue.c.
References queue_ent::announce, ao2_lock, ao2_unlock, ast_copy_string(), ast_debug, ast_log(), ast_manager_event, ast_channel::caller, queue_ent::chan, ast_channel::connected, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, ast_party_connected_line::id, ast_party_caller::id, insert_entry(), call_queue::joinempty, load_realtime_queue(), LOG_NOTICE, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, queue_ent::moh, ast_party_id::name, ast_party_id::number, queue_ent::pos, queue_ent::prio, QUEUE_FULL, QUEUE_JOINEMPTY, queue_t_unref, QUEUE_UNKNOWN, S_COR, status, ast_party_name::str, ast_party_number::str, ast_party_name::valid, and ast_party_number::valid.
Referenced by queue_exec().
02697 { 02698 struct call_queue *q; 02699 struct queue_ent *cur, *prev = NULL; 02700 int res = -1; 02701 int pos = 0; 02702 int inserted = 0; 02703 02704 if (!(q = load_realtime_queue(queuename))) 02705 return res; 02706 02707 ao2_lock(q); 02708 02709 /* This is our one */ 02710 if (q->joinempty) { 02711 int status = 0; 02712 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty, 0))) { 02713 *reason = QUEUE_JOINEMPTY; 02714 ao2_unlock(q); 02715 queue_t_unref(q, "Done with realtime queue"); 02716 return res; 02717 } 02718 } 02719 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) 02720 *reason = QUEUE_FULL; 02721 else if (*reason == QUEUE_UNKNOWN) { 02722 /* There's space for us, put us at the right position inside 02723 * the queue. 02724 * Take into account the priority of the calling user */ 02725 inserted = 0; 02726 prev = NULL; 02727 cur = q->head; 02728 while (cur) { 02729 /* We have higher priority than the current user, enter 02730 * before him, after all the other users with priority 02731 * higher or equal to our priority. */ 02732 if ((!inserted) && (qe->prio > cur->prio)) { 02733 insert_entry(q, prev, qe, &pos); 02734 inserted = 1; 02735 } 02736 /* <= is necessary for the position comparison because it may not be possible to enter 02737 * at our desired position since higher-priority callers may have taken the position we want 02738 */ 02739 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) { 02740 insert_entry(q, prev, qe, &pos); 02741 inserted = 1; 02742 /*pos is incremented inside insert_entry, so don't need to add 1 here*/ 02743 if (position < pos) { 02744 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos); 02745 } 02746 } 02747 cur->pos = ++pos; 02748 prev = cur; 02749 cur = cur->next; 02750 } 02751 /* No luck, join at the end of the queue */ 02752 if (!inserted) 02753 insert_entry(q, prev, qe, &pos); 02754 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 02755 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 02756 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 02757 q->count++; 02758 res = 0; 02759 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join", 02760 "Channel: %s\r\n" 02761 "CallerIDNum: %s\r\n" 02762 "CallerIDName: %s\r\n" 02763 "ConnectedLineNum: %s\r\n" 02764 "ConnectedLineName: %s\r\n" 02765 "Queue: %s\r\n" 02766 "Position: %d\r\n" 02767 "Count: %d\r\n" 02768 "Uniqueid: %s\r\n", 02769 qe->chan->name, 02770 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */ 02771 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 02772 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */ 02773 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"), 02774 q->name, qe->pos, q->count, qe->chan->uniqueid ); 02775 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 02776 } 02777 ao2_unlock(q); 02778 queue_t_unref(q, "Done with realtime queue"); 02779 02780 return res; 02781 }
static int kill_dead_members | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7296 of file app_queue.c.
References CMP_MATCH, member::delme, get_queue_member_status(), and member::status.
Referenced by reload_single_queue().
static int kill_dead_queues | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7440 of file app_queue.c.
References ast_strlen_zero(), CMP_MATCH, and call_queue::dead.
Referenced by reload_queues().
07441 { 07442 struct call_queue *q = obj; 07443 char *queuename = arg; 07444 if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) { 07445 return CMP_MATCH; 07446 } else { 07447 return 0; 07448 } 07449 }
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 3008 of file app_queue.c.
References ao2_lock, ao2_unlock, ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_manager_event, ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, queue_ent::parent, pbx_builtin_setvar_helper(), queue_ent::pos, 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().
03009 { 03010 struct call_queue *q; 03011 struct queue_ent *current, *prev = NULL; 03012 struct penalty_rule *pr_iter; 03013 int pos = 0; 03014 03015 if (!(q = qe->parent)) 03016 return; 03017 queue_t_ref(q, "Copy queue pointer from queue entry"); 03018 ao2_lock(q); 03019 03020 prev = NULL; 03021 for (current = q->head; current; current = current->next) { 03022 if (current == qe) { 03023 char posstr[20]; 03024 q->count--; 03025 03026 /* Take us out of the queue */ 03027 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave", 03028 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n", 03029 qe->chan->name, q->name, q->count, qe->pos, qe->chan->uniqueid); 03030 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 03031 /* Take us out of the queue */ 03032 if (prev) 03033 prev->next = current->next; 03034 else 03035 q->head = current->next; 03036 /* Free penalty rules */ 03037 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) 03038 ast_free(pr_iter); 03039 snprintf(posstr, sizeof(posstr), "%d", qe->pos); 03040 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr); 03041 } else { 03042 /* Renumber the people after us in the queue based on a new count */ 03043 current->pos = ++pos; 03044 prev = current; 03045 } 03046 } 03047 ao2_unlock(q); 03048 03049 /*If the queue is a realtime queue, check to see if it's still defined in real time*/ 03050 if (q->realtime) { 03051 struct ast_variable *var; 03052 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) { 03053 q->dead = 1; 03054 } else { 03055 ast_variables_destroy(var); 03056 } 03057 } 03058 03059 if (q->dead) { 03060 /* It's dead and nobody is in it, so kill it */ 03061 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container"); 03062 } 03063 /* unref the explicit ref earlier in the function */ 03064 queue_t_unref(q, "Expire copied reference"); 03065 }
static int load_module | ( | void | ) | [static] |
Definition at line 9120 of file app_queue.c.
References ao2_container_alloc, aqm_exec(), ARRAY_LEN, ast_add_extension2(), ast_cli_register_multiple(), ast_context_find_or_create(), ast_custom_function_register, ast_data_register_multiple, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), ast_extension_state_add(), AST_FLAGS_ALL, ast_free_ptr(), ast_log(), ast_manager_register_xml, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application_xml, ast_strdup, ast_taskprocessor_get(), device_state_cb(), EVENT_FLAG_AGENT, extension_state_cb(), LOG_ERROR, LOG_WARNING, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_log_custom(), manager_queue_member_penalty(), manager_queue_reload(), manager_queue_reset(), manager_queue_rule_show(), manager_queues_show(), manager_queues_status(), manager_queues_summary(), manager_remove_queue_member(), MAX_QUEUE_BUCKETS, pqm_exec(), ql_exec(), queue_cmp_cb(), queue_exec(), queue_hash_cb(), queues, reload_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().
09121 { 09122 int res; 09123 struct ast_context *con; 09124 struct ast_flags mask = {AST_FLAGS_ALL, }; 09125 09126 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb); 09127 09128 use_weight = 0; 09129 09130 if (reload_handler(0, &mask, NULL)) 09131 return AST_MODULE_LOAD_DECLINE; 09132 09133 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue"); 09134 if (!con) 09135 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n"); 09136 else 09137 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue"); 09138 09139 if (queue_persistent_members) 09140 reload_queue_members(); 09141 09142 ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers)); 09143 09144 ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue)); 09145 res = ast_register_application_xml(app, queue_exec); 09146 res |= ast_register_application_xml(app_aqm, aqm_exec); 09147 res |= ast_register_application_xml(app_rqm, rqm_exec); 09148 res |= ast_register_application_xml(app_pqm, pqm_exec); 09149 res |= ast_register_application_xml(app_upqm, upqm_exec); 09150 res |= ast_register_application_xml(app_ql, ql_exec); 09151 res |= ast_manager_register_xml("Queues", 0, manager_queues_show); 09152 res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status); 09153 res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary); 09154 res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member); 09155 res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member); 09156 res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member); 09157 res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom); 09158 res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty); 09159 res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show); 09160 res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload); 09161 res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset); 09162 res |= ast_custom_function_register(&queuevar_function); 09163 res |= ast_custom_function_register(&queueexists_function); 09164 res |= ast_custom_function_register(&queuemembercount_function); 09165 res |= ast_custom_function_register(&queuemembercount_dep); 09166 res |= ast_custom_function_register(&queuememberlist_function); 09167 res |= ast_custom_function_register(&queuewaitingcount_function); 09168 res |= ast_custom_function_register(&queuememberpenalty_function); 09169 res |= ast_custom_function_register(&queuememberstatus_function); 09170 res |= ast_custom_function_register(&queuememberpaused_function); 09171 09172 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) { 09173 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); 09174 } 09175 09176 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */ 09177 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) { 09178 res = -1; 09179 } 09180 09181 ast_extension_state_add(NULL, NULL, extension_state_cb, NULL); 09182 09183 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL); 09184 09185 return res ? AST_MODULE_LOAD_DECLINE : 0; 09186 }
static struct call_queue* load_realtime_queue | ( | const char * | queuename | ) | [static, read] |
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 2569 of file app_queue.c.
References ao2_t_find, ast_atomic_fetchadd_int(), ast_config_destroy(), ast_config_new(), ast_debug, ast_load_realtime(), ast_load_realtime_multientry(), ast_variables_destroy(), find_queue_by_name_rt(), OBJ_POINTER, queue_t_unref, queues, call_queue::realtime, SENTINEL, update_realtime_members(), and call_queue::weight.
Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_exists(), queue_function_qac(), queue_function_qac_dep(), queues_data_provider_get(), and reload_queue_members().
02570 { 02571 struct ast_variable *queue_vars; 02572 struct ast_config *member_config = NULL; 02573 struct call_queue *q = NULL, tmpq = { 02574 .name = queuename, 02575 }; 02576 int prev_weight = 0; 02577 02578 /* Find the queue in the in-core list first. */ 02579 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first"); 02580 02581 if (!q || q->realtime) { 02582 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all 02583 queue operations while waiting for the DB. 02584 02585 This will be two separate database transactions, so we might 02586 see queue parameters as they were before another process 02587 changed the queue and member list as it was after the change. 02588 Thus we might see an empty member list when a queue is 02589 deleted. In practise, this is unlikely to cause a problem. */ 02590 02591 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL); 02592 if (queue_vars) { 02593 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL); 02594 if (!member_config) { 02595 ast_debug(1, "No queue_members defined in config extconfig.conf\n"); 02596 member_config = ast_config_new(); 02597 } 02598 } 02599 if (q) { 02600 prev_weight = q->weight ? 1 : 0; 02601 queue_t_unref(q, "Need to find realtime queue"); 02602 } 02603 02604 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 02605 ast_config_destroy(member_config); 02606 ast_variables_destroy(queue_vars); 02607 02608 /* update the use_weight value if the queue's has gained or lost a weight */ 02609 if (q) { 02610 if (!q->weight && prev_weight) { 02611 ast_atomic_fetchadd_int(&use_weight, -1); 02612 } 02613 if (q->weight && !prev_weight) { 02614 ast_atomic_fetchadd_int(&use_weight, +1); 02615 } 02616 } 02617 /* Other cases will end up with the proper value for use_weight */ 02618 } else { 02619 update_realtime_members(q); 02620 } 02621 return q; 02622 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8079 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().
08080 { 08081 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface; 08082 int paused, penalty = 0; 08083 08084 queuename = astman_get_header(m, "Queue"); 08085 interface = astman_get_header(m, "Interface"); 08086 penalty_s = astman_get_header(m, "Penalty"); 08087 paused_s = astman_get_header(m, "Paused"); 08088 membername = astman_get_header(m, "MemberName"); 08089 state_interface = astman_get_header(m, "StateInterface"); 08090 08091 if (ast_strlen_zero(queuename)) { 08092 astman_send_error(s, m, "'Queue' not specified."); 08093 return 0; 08094 } 08095 08096 if (ast_strlen_zero(interface)) { 08097 astman_send_error(s, m, "'Interface' not specified."); 08098 return 0; 08099 } 08100 08101 if (ast_strlen_zero(penalty_s)) 08102 penalty = 0; 08103 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) 08104 penalty = 0; 08105 08106 if (ast_strlen_zero(paused_s)) 08107 paused = 0; 08108 else 08109 paused = abs(ast_true(paused_s)); 08110 08111 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) { 08112 case RES_OKAY: 08113 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : ""); 08114 astman_send_ack(s, m, "Added interface to queue"); 08115 break; 08116 case RES_EXISTS: 08117 astman_send_error(s, m, "Unable to add interface: Already there"); 08118 break; 08119 case RES_NOSUCHQUEUE: 08120 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 08121 break; 08122 case RES_OUTOFMEMORY: 08123 astman_send_error(s, m, "Out of memory"); 08124 break; 08125 } 08126 08127 return 0; 08128 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8164 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().
08165 { 08166 const char *queuename, *interface, *paused_s, *reason; 08167 int paused; 08168 08169 interface = astman_get_header(m, "Interface"); 08170 paused_s = astman_get_header(m, "Paused"); 08171 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 08172 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */ 08173 08174 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 08175 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 08176 return 0; 08177 } 08178 08179 paused = abs(ast_true(paused_s)); 08180 08181 if (set_member_paused(queuename, interface, reason, paused)) 08182 astman_send_error(s, m, "Interface not found"); 08183 else 08184 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 08185 return 0; 08186 }
static int manager_queue_log_custom | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8188 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().
08189 { 08190 const char *queuename, *event, *message, *interface, *uniqueid; 08191 08192 queuename = astman_get_header(m, "Queue"); 08193 uniqueid = astman_get_header(m, "UniqueId"); 08194 interface = astman_get_header(m, "Interface"); 08195 event = astman_get_header(m, "Event"); 08196 message = astman_get_header(m, "Message"); 08197 08198 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) { 08199 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters."); 08200 return 0; 08201 } 08202 08203 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message); 08204 astman_send_ack(s, m, "Event added successfully"); 08205 08206 return 0; 08207 }
static int manager_queue_member_penalty | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8287 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().
08288 { 08289 const char *queuename, *interface, *penalty_s; 08290 int penalty; 08291 08292 interface = astman_get_header(m, "Interface"); 08293 penalty_s = astman_get_header(m, "Penalty"); 08294 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ 08295 queuename = astman_get_header(m, "Queue"); 08296 08297 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { 08298 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); 08299 return 0; 08300 } 08301 08302 penalty = atoi(penalty_s); 08303 08304 if (set_member_penalty((char *)queuename, (char *)interface, penalty)) 08305 astman_send_error(s, m, "Invalid interface, queuename or penalty"); 08306 else 08307 astman_send_ack(s, m, "Interface penalty set successfully"); 08308 08309 return 0; 08310 }
static int manager_queue_reload | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8209 of file app_queue.c.
References AST_FLAGS_ALL, ast_set_flag, astman_get_header(), astman_send_ack(), astman_send_error(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, reload_handler(), and S_OR.
Referenced by load_module().
08210 { 08211 struct ast_flags mask = {0,}; 08212 const char *queuename = NULL; 08213 int header_found = 0; 08214 08215 queuename = astman_get_header(m, "Queue"); 08216 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) { 08217 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 08218 header_found = 1; 08219 } 08220 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) { 08221 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 08222 header_found = 1; 08223 } 08224 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) { 08225 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 08226 header_found = 1; 08227 } 08228 08229 if (!header_found) { 08230 ast_set_flag(&mask, AST_FLAGS_ALL); 08231 } 08232 08233 if (!reload_handler(1, &mask, queuename)) { 08234 astman_send_ack(s, m, "Queue reloaded successfully"); 08235 } else { 08236 astman_send_error(s, m, "Error encountered while reloading queue"); 08237 } 08238 return 0; 08239 }
static int manager_queue_reset | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8241 of file app_queue.c.
References astman_get_header(), astman_send_ack(), astman_send_error(), QUEUE_RESET_STATS, and reload_handler().
Referenced by load_module().
08242 { 08243 const char *queuename = NULL; 08244 struct ast_flags mask = {QUEUE_RESET_STATS,}; 08245 08246 queuename = astman_get_header(m, "Queue"); 08247 08248 if (!reload_handler(1, &mask, queuename)) { 08249 astman_send_ack(s, m, "Queue stats reset successfully"); 08250 } else { 08251 astman_send_error(s, m, "Error encountered while resetting queue stats"); 08252 } 08253 return 0; 08254 }
static int manager_queue_rule_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7867 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, and penalty_rule::time.
Referenced by load_module().
07868 { 07869 const char *rule = astman_get_header(m, "Rule"); 07870 const char *id = astman_get_header(m, "ActionID"); 07871 struct rule_list *rl_iter; 07872 struct penalty_rule *pr_iter; 07873 07874 astman_append(s, "Response: Success\r\n"); 07875 if (!ast_strlen_zero(id)) { 07876 astman_append(s, "ActionID: %s\r\n", id); 07877 } 07878 07879 AST_LIST_LOCK(&rule_lists); 07880 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 07881 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) { 07882 astman_append(s, "RuleList: %s\r\n", rl_iter->name); 07883 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 07884 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 ); 07885 } 07886 if (!ast_strlen_zero(rule)) 07887 break; 07888 } 07889 } 07890 AST_LIST_UNLOCK(&rule_lists); 07891 07892 /* 07893 * Two blank lines instead of one because the Response and 07894 * ActionID headers used to not be present. 07895 */ 07896 astman_append(s, "\r\n\r\n"); 07897 07898 return RESULT_SUCCESS; 07899 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7857 of file app_queue.c.
References __queues_show(), astman_append(), and RESULT_SUCCESS.
Referenced by load_module().
07858 { 07859 static const char * const a[] = { "queue", "show" }; 07860 07861 __queues_show(s, -1, 2, a); 07862 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 07863 07864 return RESULT_SUCCESS; 07865 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Queue status info via AMI.
Definition at line 7977 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), ast_channel::caller, member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::connected, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, ast_party_connected_line::id, ast_party_caller::id, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, ast_party_id::name, ast_party_id::number, member::paused, member::penalty, queue_ent::pos, queue_t_unref, queues, RESULT_SUCCESS, call_queue::ringlimit, S_COR, call_queue::servicelevel, queue_ent::start, member::status, ast_party_name::str, ast_party_number::str, call_queue::strategy, call_queue::talktime, ast_party_name::valid, ast_party_number::valid, and call_queue::weight.
Referenced by load_module().
07978 { 07979 time_t now; 07980 int pos; 07981 const char *id = astman_get_header(m,"ActionID"); 07982 const char *queuefilter = astman_get_header(m,"Queue"); 07983 const char *memberfilter = astman_get_header(m,"Member"); 07984 char idText[256] = ""; 07985 struct call_queue *q; 07986 struct queue_ent *qe; 07987 float sl = 0; 07988 struct member *mem; 07989 struct ao2_iterator queue_iter; 07990 struct ao2_iterator mem_iter; 07991 07992 astman_send_ack(s, m, "Queue status will follow"); 07993 time(&now); 07994 if (!ast_strlen_zero(id)) 07995 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 07996 07997 queue_iter = ao2_iterator_init(queues, 0); 07998 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07999 ao2_lock(q); 08000 08001 /* List queue properties */ 08002 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 08003 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 08004 astman_append(s, "Event: QueueParams\r\n" 08005 "Queue: %s\r\n" 08006 "Max: %d\r\n" 08007 "Strategy: %s\r\n" 08008 "Calls: %d\r\n" 08009 "Holdtime: %d\r\n" 08010 "TalkTime: %d\r\n" 08011 "Completed: %d\r\n" 08012 "Abandoned: %d\r\n" 08013 "ServiceLevel: %d\r\n" 08014 "ServicelevelPerf: %2.1f\r\n" 08015 "RingLimit: %d\r\n" 08016 "Weight: %d\r\n" 08017 "%s" 08018 "\r\n", 08019 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, 08020 q->callsabandoned, q->servicelevel, sl, q->ringlimit, q->weight, idText); 08021 /* List Queue Members */ 08022 mem_iter = ao2_iterator_init(q->members, 0); 08023 while ((mem = ao2_iterator_next(&mem_iter))) { 08024 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) { 08025 astman_append(s, "Event: QueueMember\r\n" 08026 "Queue: %s\r\n" 08027 "Name: %s\r\n" 08028 "Location: %s\r\n" 08029 "Membership: %s\r\n" 08030 "Penalty: %d\r\n" 08031 "CallsTaken: %d\r\n" 08032 "LastCall: %d\r\n" 08033 "Status: %d\r\n" 08034 "Paused: %d\r\n" 08035 "%s" 08036 "\r\n", 08037 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 08038 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 08039 } 08040 ao2_ref(mem, -1); 08041 } 08042 ao2_iterator_destroy(&mem_iter); 08043 /* List Queue Entries */ 08044 pos = 1; 08045 for (qe = q->head; qe; qe = qe->next) { 08046 astman_append(s, "Event: QueueEntry\r\n" 08047 "Queue: %s\r\n" 08048 "Position: %d\r\n" 08049 "Channel: %s\r\n" 08050 "Uniqueid: %s\r\n" 08051 "CallerIDNum: %s\r\n" 08052 "CallerIDName: %s\r\n" 08053 "ConnectedLineNum: %s\r\n" 08054 "ConnectedLineName: %s\r\n" 08055 "Wait: %ld\r\n" 08056 "%s" 08057 "\r\n", 08058 q->name, pos++, qe->chan->name, qe->chan->uniqueid, 08059 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"), 08060 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 08061 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"), 08062 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"), 08063 (long) (now - qe->start), idText); 08064 } 08065 } 08066 ao2_unlock(q); 08067 queue_t_unref(q, "Done with iterator"); 08068 } 08069 ao2_iterator_destroy(&queue_iter); 08070 08071 astman_append(s, 08072 "Event: QueueStatusComplete\r\n" 08073 "%s" 08074 "\r\n",idText); 08075 08076 return RESULT_SUCCESS; 08077 }
static int manager_queues_summary | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Summary of queue info via the AMI.
Definition at line 7902 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_UNAVAILABLE, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), call_queue::head, call_queue::holdtime, member_status_available(), call_queue::members, member::paused, queue_t_unref, queues, RESULT_SUCCESS, queue_ent::start, member::status, and call_queue::talktime.
Referenced by load_module().
07903 { 07904 time_t now; 07905 int qmemcount = 0; 07906 int qmemavail = 0; 07907 int qchancount = 0; 07908 int qlongestholdtime = 0; 07909 const char *id = astman_get_header(m, "ActionID"); 07910 const char *queuefilter = astman_get_header(m, "Queue"); 07911 char idText[256] = ""; 07912 struct call_queue *q; 07913 struct queue_ent *qe; 07914 struct member *mem; 07915 struct ao2_iterator queue_iter; 07916 struct ao2_iterator mem_iter; 07917 07918 astman_send_ack(s, m, "Queue summary will follow"); 07919 time(&now); 07920 if (!ast_strlen_zero(id)) 07921 snprintf(idText, 256, "ActionID: %s\r\n", id); 07922 queue_iter = ao2_iterator_init(queues, 0); 07923 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07924 ao2_lock(q); 07925 07926 /* List queue properties */ 07927 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 07928 /* Reset the necessary local variables if no queuefilter is set*/ 07929 qmemcount = 0; 07930 qmemavail = 0; 07931 qchancount = 0; 07932 qlongestholdtime = 0; 07933 07934 /* List Queue Members */ 07935 mem_iter = ao2_iterator_init(q->members, 0); 07936 while ((mem = ao2_iterator_next(&mem_iter))) { 07937 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) { 07938 ++qmemcount; 07939 if (member_status_available(mem->status) && !mem->paused) { 07940 ++qmemavail; 07941 } 07942 } 07943 ao2_ref(mem, -1); 07944 } 07945 ao2_iterator_destroy(&mem_iter); 07946 for (qe = q->head; qe; qe = qe->next) { 07947 if ((now - qe->start) > qlongestholdtime) { 07948 qlongestholdtime = now - qe->start; 07949 } 07950 ++qchancount; 07951 } 07952 astman_append(s, "Event: QueueSummary\r\n" 07953 "Queue: %s\r\n" 07954 "LoggedIn: %d\r\n" 07955 "Available: %d\r\n" 07956 "Callers: %d\r\n" 07957 "HoldTime: %d\r\n" 07958 "TalkTime: %d\r\n" 07959 "LongestHoldTime: %d\r\n" 07960 "%s" 07961 "\r\n", 07962 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText); 07963 } 07964 ao2_unlock(q); 07965 queue_t_unref(q, "Done with iterator"); 07966 } 07967 ao2_iterator_destroy(&queue_iter); 07968 astman_append(s, 07969 "Event: QueueSummaryComplete\r\n" 07970 "%s" 07971 "\r\n", idText); 07972 07973 return RESULT_SUCCESS; 07974 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 8130 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().
08131 { 08132 const char *queuename, *interface; 08133 08134 queuename = astman_get_header(m, "Queue"); 08135 interface = astman_get_header(m, "Interface"); 08136 08137 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 08138 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 08139 return 0; 08140 } 08141 08142 switch (remove_from_queue(queuename, interface)) { 08143 case RES_OKAY: 08144 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 08145 astman_send_ack(s, m, "Removed interface from queue"); 08146 break; 08147 case RES_EXISTS: 08148 astman_send_error(s, m, "Unable to remove interface: Not there"); 08149 break; 08150 case RES_NOSUCHQUEUE: 08151 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 08152 break; 08153 case RES_OUTOFMEMORY: 08154 astman_send_error(s, m, "Out of memory"); 08155 break; 08156 case RES_NOT_DYNAMIC: 08157 astman_send_error(s, m, "Member not dynamic"); 08158 break; 08159 } 08160 08161 return 0; 08162 }
static int mark_dead_and_unfound | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7429 of file app_queue.c.
References ast_strlen_zero(), call_queue::dead, call_queue::found, and call_queue::realtime.
Referenced by reload_queues().
07430 { 07431 struct call_queue *q = obj; 07432 char *queuename = arg; 07433 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) { 07434 q->dead = 1; 07435 q->found = 0; 07436 } 07437 return 0; 07438 }
static int mark_member_dead | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7287 of file app_queue.c.
References member::delme, member::dynamic, and member::realtime.
Referenced by reload_single_queue().
static void member_add_to_queue | ( | struct call_queue * | queue, | |
struct member * | mem | |||
) | [static] |
Definition at line 2296 of file app_queue.c.
References ao2_container_count(), ao2_link, ao2_lock, ao2_unlock, call_queue::members, and member::queuepos.
Referenced by add_to_queue(), reload_single_member(), and rt_handle_member_record().
02297 { 02298 ao2_lock(queue->members); 02299 mem->queuepos = ao2_container_count(queue->members); 02300 ao2_link(queue->members, mem); 02301 ao2_unlock(queue->members); 02302 }
static void member_call_pending_clear | ( | struct member * | mem | ) | [static] |
Definition at line 3260 of file app_queue.c.
References ao2_lock, ao2_unlock, and member::call_pending.
Referenced by can_ring_entry(), and ring_entry().
03261 { 03262 ao2_lock(mem); 03263 mem->call_pending = 0; 03264 ao2_unlock(mem); 03265 }
static int member_call_pending_set | ( | struct member * | mem | ) | [static] |
Definition at line 3275 of file app_queue.c.
References ao2_lock, ao2_unlock, and member::call_pending.
Referenced by can_ring_entry().
03276 { 03277 int old_pending; 03278 03279 ao2_lock(mem); 03280 old_pending = mem->call_pending; 03281 mem->call_pending = 1; 03282 ao2_unlock(mem); 03283 03284 return old_pending; 03285 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 1851 of file app_queue.c.
References CMP_MATCH, CMP_STOP, and member::interface.
Referenced by init_queue().
static int member_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1839 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
01840 { 01841 const struct member *mem = obj; 01842 const char *chname = strchr(mem->interface, '/'); 01843 int ret = 0, i; 01844 if (!chname) 01845 chname = mem->interface; 01846 for (i = 0; i < 5 && chname[i]; i++) 01847 ret += compress_char(chname[i]) << (i * 6); 01848 return ret; 01849 }
static void member_remove_from_queue | ( | struct call_queue * | queue, | |
struct member * | mem | |||
) | [static] |
Definition at line 2310 of file app_queue.c.
References ao2_lock, ao2_unlink, ao2_unlock, call_queue::members, and queue_member_follower_removal().
Referenced by find_queue_by_name_rt(), free_members(), remove_from_queue(), and update_realtime_members().
02311 { 02312 ao2_lock(queue->members); 02313 queue_member_follower_removal(queue, mem); 02314 ao2_unlink(queue->members, mem); 02315 ao2_unlock(queue->members); 02316 }
static int member_status_available | ( | int | status | ) | [static] |
Definition at line 3247 of file app_queue.c.
References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.
Referenced by can_ring_entry(), and manager_queues_summary().
03248 { 03249 return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN; 03250 }
static int num_available_members | ( | struct call_queue * | q | ) | [static] |
Get the number of members available to accept a call.
[in] | q | The queue for which we are couting the number of available members |
Definition at line 3113 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_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, 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().
03114 { 03115 struct member *mem; 03116 int avl = 0; 03117 struct ao2_iterator mem_iter; 03118 03119 mem_iter = ao2_iterator_init(q->members, 0); 03120 while ((mem = ao2_iterator_next(&mem_iter))) { 03121 switch (mem->status) { 03122 case AST_DEVICE_INUSE: 03123 if (!q->ringinuse) 03124 break; 03125 /* else fall through */ 03126 case AST_DEVICE_NOT_INUSE: 03127 case AST_DEVICE_ONHOLD: 03128 case AST_DEVICE_RINGINUSE: 03129 case AST_DEVICE_RINGING: 03130 case AST_DEVICE_UNKNOWN: 03131 if (!mem->paused) { 03132 avl++; 03133 } 03134 break; 03135 } 03136 ao2_ref(mem, -1); 03137 03138 /* If autofill is not enabled or if the queue's strategy is ringall, then 03139 * we really don't care about the number of available members so much as we 03140 * do that there is at least one available. 03141 * 03142 * In fact, we purposely will return from this function stating that only 03143 * one member is available if either of those conditions hold. That way, 03144 * functions which determine what action to take based on the number of available 03145 * members will operate properly. The reasoning is that even if multiple 03146 * members are available, only the head caller can actually be serviced. 03147 */ 03148 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { 03149 break; 03150 } 03151 } 03152 ao2_iterator_destroy(&mem_iter); 03153 03154 return avl; 03155 }
static void parse_empty_options | ( | const char * | value, | |
enum empty_conditions * | empty, | |||
int | joinempty | |||
) | [static] |
Definition at line 2046 of file app_queue.c.
References ast_false(), ast_log(), ast_strdupa, ast_true(), LOG_WARNING, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_RINGING, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, and QUEUE_EMPTY_WRAPUP.
Referenced by queue_set_param().
02047 { 02048 char *value_copy = ast_strdupa(value); 02049 char *option = NULL; 02050 while ((option = strsep(&value_copy, ","))) { 02051 if (!strcasecmp(option, "paused")) { 02052 *empty |= QUEUE_EMPTY_PAUSED; 02053 } else if (!strcasecmp(option, "penalty")) { 02054 *empty |= QUEUE_EMPTY_PENALTY; 02055 } else if (!strcasecmp(option, "inuse")) { 02056 *empty |= QUEUE_EMPTY_INUSE; 02057 } else if (!strcasecmp(option, "ringing")) { 02058 *empty |= QUEUE_EMPTY_RINGING; 02059 } else if (!strcasecmp(option, "invalid")) { 02060 *empty |= QUEUE_EMPTY_INVALID; 02061 } else if (!strcasecmp(option, "wrapup")) { 02062 *empty |= QUEUE_EMPTY_WRAPUP; 02063 } else if (!strcasecmp(option, "unavailable")) { 02064 *empty |= QUEUE_EMPTY_UNAVAILABLE; 02065 } else if (!strcasecmp(option, "unknown")) { 02066 *empty |= QUEUE_EMPTY_UNKNOWN; 02067 } else if (!strcasecmp(option, "loose")) { 02068 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID); 02069 } else if (!strcasecmp(option, "strict")) { 02070 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE); 02071 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) { 02072 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED); 02073 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) { 02074 *empty = 0; 02075 } else { 02076 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty"); 02077 } 02078 } 02079 }
static int play_file | ( | struct ast_channel * | chan, | |
const char * | filename | |||
) | [static] |
Definition at line 2783 of file app_queue.c.
References AST_DIGIT_ANY, ast_fileexists(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), and ast_waitstream().
Referenced by say_periodic_announcement(), say_position(), and try_calling().
02784 { 02785 int res; 02786 02787 if (ast_strlen_zero(filename)) { 02788 return 0; 02789 } 02790 02791 if (!ast_fileexists(filename, NULL, chan->language)) { 02792 return 0; 02793 } 02794 02795 ast_stopstream(chan); 02796 02797 res = ast_streamfile(chan, filename, chan->language); 02798 if (!res) 02799 res = ast_waitstream(chan, AST_DIGIT_ANY); 02800 02801 ast_stopstream(chan); 02802 02803 return res; 02804 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
PauseQueueMember application.
Definition at line 6063 of file app_queue.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
06064 { 06065 char *parse; 06066 AST_DECLARE_APP_ARGS(args, 06067 AST_APP_ARG(queuename); 06068 AST_APP_ARG(interface); 06069 AST_APP_ARG(options); 06070 AST_APP_ARG(reason); 06071 ); 06072 06073 if (ast_strlen_zero(data)) { 06074 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n"); 06075 return -1; 06076 } 06077 06078 parse = ast_strdupa(data); 06079 06080 AST_STANDARD_APP_ARGS(args, parse); 06081 06082 if (ast_strlen_zero(args.interface)) { 06083 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 06084 return -1; 06085 } 06086 06087 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) { 06088 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 06089 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 06090 return 0; 06091 } 06092 06093 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 06094 06095 return 0; 06096 }
static int ql_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
QueueLog application.
Definition at line 6254 of file app_queue.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, and parse().
Referenced by load_module().
06255 { 06256 char *parse; 06257 06258 AST_DECLARE_APP_ARGS(args, 06259 AST_APP_ARG(queuename); 06260 AST_APP_ARG(uniqueid); 06261 AST_APP_ARG(membername); 06262 AST_APP_ARG(event); 06263 AST_APP_ARG(params); 06264 ); 06265 06266 if (ast_strlen_zero(data)) { 06267 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n"); 06268 return -1; 06269 } 06270 06271 parse = ast_strdupa(data); 06272 06273 AST_STANDARD_APP_ARGS(args, parse); 06274 06275 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 06276 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 06277 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n"); 06278 return -1; 06279 } 06280 06281 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 06282 "%s", args.params ? args.params : ""); 06283 06284 return 0; 06285 }
static int queue_cmp_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1400 of file app_queue.c.
References CMP_MATCH, and CMP_STOP.
Referenced by load_module().
01401 { 01402 struct call_queue *q = obj, *q2 = arg; 01403 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0; 01404 }
static int queue_delme_members_decrement_followers | ( | void * | obj, | |
void * | arg, | |||
int | flag | |||
) | [static] |
Definition at line 1429 of file app_queue.c.
References ao2_callback, member::delme, call_queue::members, OBJ_MULTIPLE, OBJ_NODATA, queue_member_decrement_followers(), member::queuepos, and call_queue::rrpos.
Referenced by reload_single_queue().
01430 { 01431 struct member *mem = obj; 01432 struct call_queue *queue = arg; 01433 int rrpos = mem->queuepos; 01434 01435 if (mem->delme) { 01436 ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &rrpos); 01437 } 01438 01439 return 0; 01440 }
static int queue_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
The starting point for all queue calls.
The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again unless some condition (such as an expiration time) causes us to exit the queue.
Definition at line 6328 of file app_queue.c.
References call_queue::announcefrequency, ao2_container_count(), args, AST_APP_ARG, ast_assert, ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), AST_LIST_FIRST, ast_log(), ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::chan, copy_rules(), queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, ast_party_caller::id, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::members, queue_ent::min_penalty, queue_ent::moh, ast_party_id::number, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, QUEUE_CONTINUE, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, queue_unref(), record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), set_queue_variables(), queue_ent::start, status, stop, ast_party_number::str, queue_ent::tries, try_calling(), update_qe_rule(), update_realtime_members(), url, ast_party_number::valid, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().
Referenced by load_module().
06329 { 06330 int res=-1; 06331 int ringing=0; 06332 const char *user_priority; 06333 const char *max_penalty_str; 06334 const char *min_penalty_str; 06335 int prio; 06336 int qcontinue = 0; 06337 int max_penalty, min_penalty; 06338 enum queue_result reason = QUEUE_UNKNOWN; 06339 /* whether to exit Queue application after the timeout hits */ 06340 int tries = 0; 06341 int noption = 0; 06342 char *parse; 06343 int makeannouncement = 0; 06344 int position = 0; 06345 AST_DECLARE_APP_ARGS(args, 06346 AST_APP_ARG(queuename); 06347 AST_APP_ARG(options); 06348 AST_APP_ARG(url); 06349 AST_APP_ARG(announceoverride); 06350 AST_APP_ARG(queuetimeoutstr); 06351 AST_APP_ARG(agi); 06352 AST_APP_ARG(macro); 06353 AST_APP_ARG(gosub); 06354 AST_APP_ARG(rule); 06355 AST_APP_ARG(position); 06356 ); 06357 /* Our queue entry */ 06358 struct queue_ent qe = { 0 }; 06359 06360 if (ast_strlen_zero(data)) { 06361 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n"); 06362 return -1; 06363 } 06364 06365 parse = ast_strdupa(data); 06366 AST_STANDARD_APP_ARGS(args, parse); 06367 06368 /* Setup our queue entry */ 06369 qe.start = time(NULL); 06370 06371 /* set the expire time based on the supplied timeout; */ 06372 if (!ast_strlen_zero(args.queuetimeoutstr)) 06373 qe.expire = qe.start + atoi(args.queuetimeoutstr); 06374 else 06375 qe.expire = 0; 06376 06377 /* Get the priority from the variable ${QUEUE_PRIO} */ 06378 ast_channel_lock(chan); 06379 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 06380 if (user_priority) { 06381 if (sscanf(user_priority, "%30d", &prio) == 1) { 06382 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio); 06383 } else { 06384 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 06385 user_priority, chan->name); 06386 prio = 0; 06387 } 06388 } else { 06389 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n"); 06390 prio = 0; 06391 } 06392 06393 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 06394 06395 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 06396 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) { 06397 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty); 06398 } else { 06399 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 06400 max_penalty_str, chan->name); 06401 max_penalty = INT_MAX; 06402 } 06403 } else { 06404 max_penalty = INT_MAX; 06405 } 06406 06407 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) { 06408 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) { 06409 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty); 06410 } else { 06411 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n", 06412 min_penalty_str, chan->name); 06413 min_penalty = INT_MAX; 06414 } 06415 } else { 06416 min_penalty = INT_MAX; 06417 } 06418 ast_channel_unlock(chan); 06419 06420 if (args.options && (strchr(args.options, 'r'))) 06421 ringing = 1; 06422 06423 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) { 06424 qe.ring_when_ringing = 1; 06425 } 06426 06427 if (args.options && (strchr(args.options, 'c'))) 06428 qcontinue = 1; 06429 06430 if (args.position) { 06431 position = atoi(args.position); 06432 if (position < 0) { 06433 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename); 06434 position = 0; 06435 } 06436 } 06437 06438 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 06439 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 06440 06441 qe.chan = chan; 06442 qe.prio = prio; 06443 qe.max_penalty = max_penalty; 06444 qe.min_penalty = min_penalty; 06445 qe.last_pos_said = 0; 06446 qe.last_pos = 0; 06447 qe.last_periodic_announce_time = time(NULL); 06448 qe.last_periodic_announce_sound = 0; 06449 qe.valid_digits = 0; 06450 if (join_queue(args.queuename, &qe, &reason, position)) { 06451 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 06452 set_queue_result(chan, reason); 06453 return 0; 06454 } 06455 ast_assert(qe.parent != NULL); 06456 06457 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d", 06458 S_OR(args.url, ""), 06459 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""), 06460 qe.opos); 06461 copy_rules(&qe, args.rule); 06462 qe.pr = AST_LIST_FIRST(&qe.qe_rules); 06463 check_turns: 06464 if (ringing) { 06465 ast_indicate(chan, AST_CONTROL_RINGING); 06466 } else { 06467 ast_moh_start(chan, qe.moh, NULL); 06468 } 06469 06470 /* This is the wait loop for callers 2 through maxlen */ 06471 res = wait_our_turn(&qe, ringing, &reason); 06472 if (res) { 06473 goto stop; 06474 } 06475 06476 makeannouncement = 0; 06477 06478 for (;;) { 06479 /* This is the wait loop for the head caller*/ 06480 /* To exit, they may get their call answered; */ 06481 /* they may dial a digit from the queue context; */ 06482 /* or, they may timeout. */ 06483 06484 /* Leave if we have exceeded our queuetimeout */ 06485 if (qe.expire && (time(NULL) >= qe.expire)) { 06486 record_abandoned(&qe); 06487 reason = QUEUE_TIMEOUT; 06488 res = 0; 06489 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 06490 qe.pos, qe.opos, (long) time(NULL) - qe.start); 06491 break; 06492 } 06493 06494 if (makeannouncement) { 06495 /* Make a position announcement, if enabled */ 06496 if (qe.parent->announcefrequency) 06497 if ((res = say_position(&qe,ringing))) 06498 goto stop; 06499 } 06500 makeannouncement = 1; 06501 06502 /* Make a periodic announcement, if enabled */ 06503 if (qe.parent->periodicannouncefrequency) 06504 if ((res = say_periodic_announcement(&qe,ringing))) 06505 goto stop; 06506 06507 /* Leave if we have exceeded our queuetimeout */ 06508 if (qe.expire && (time(NULL) >= qe.expire)) { 06509 record_abandoned(&qe); 06510 reason = QUEUE_TIMEOUT; 06511 res = 0; 06512 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 06513 break; 06514 } 06515 06516 /* see if we need to move to the next penalty level for this queue */ 06517 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) { 06518 update_qe_rule(&qe); 06519 } 06520 06521 /* Try calling all queue members for 'timeout' seconds */ 06522 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing); 06523 if (res) { 06524 goto stop; 06525 } 06526 06527 if (qe.parent->leavewhenempty) { 06528 int status = 0; 06529 if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty, 0))) { 06530 record_abandoned(&qe); 06531 reason = QUEUE_LEAVEEMPTY; 06532 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 06533 res = 0; 06534 break; 06535 } 06536 } 06537 06538 /* exit after 'timeout' cycle if 'n' option enabled */ 06539 if (noption && tries >= ao2_container_count(qe.parent->members)) { 06540 ast_verb(3, "Exiting on time-out cycle\n"); 06541 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 06542 record_abandoned(&qe); 06543 reason = QUEUE_TIMEOUT; 06544 res = 0; 06545 break; 06546 } 06547 06548 06549 /* Leave if we have exceeded our queuetimeout */ 06550 if (qe.expire && (time(NULL) >= qe.expire)) { 06551 record_abandoned(&qe); 06552 reason = QUEUE_TIMEOUT; 06553 res = 0; 06554 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start); 06555 break; 06556 } 06557 06558 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 06559 update_realtime_members(qe.parent); 06560 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 06561 res = wait_a_bit(&qe); 06562 if (res) 06563 goto stop; 06564 06565 /* Since this is a priority queue and 06566 * it is not sure that we are still at the head 06567 * of the queue, go and check for our turn again. 06568 */ 06569 if (!is_our_turn(&qe)) { 06570 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name); 06571 goto check_turns; 06572 } 06573 } 06574 06575 stop: 06576 if (res) { 06577 if (res < 0) { 06578 if (!qe.handled) { 06579 record_abandoned(&qe); 06580 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 06581 "%d|%d|%ld", qe.pos, qe.opos, 06582 (long) time(NULL) - qe.start); 06583 res = -1; 06584 } else if (qcontinue) { 06585 reason = QUEUE_CONTINUE; 06586 res = 0; 06587 } 06588 } else if (qe.valid_digits) { 06589 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 06590 "%s|%d|%d|%ld", qe.digits, qe.pos, qe.opos, (long) time(NULL) - qe.start); 06591 } 06592 } 06593 06594 /* Don't allow return code > 0 */ 06595 if (res >= 0) { 06596 res = 0; 06597 if (ringing) { 06598 ast_indicate(chan, -1); 06599 } else { 06600 ast_moh_stop(chan); 06601 } 06602 ast_stopstream(chan); 06603 } 06604 06605 set_queue_variables(qe.parent, qe.chan); 06606 06607 leave_queue(&qe); 06608 if (reason != QUEUE_UNKNOWN) 06609 set_queue_result(chan, reason); 06610 06611 /* 06612 * every queue_ent is given a reference to it's parent 06613 * call_queue when it joins the queue. This ref must be taken 06614 * away right before the queue_ent is destroyed. In this case 06615 * the queue_ent is about to be returned on the stack 06616 */ 06617 qe.parent = queue_unref(qe.parent); 06618 06619 return res; 06620 }
static int queue_function_exists | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Check if a given queue exists.
Definition at line 6674 of file app_queue.c.
References ast_log(), ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, and queue_t_unref.
06675 { 06676 struct call_queue *q; 06677 06678 buf[0] = '\0'; 06679 06680 if (ast_strlen_zero(data)) { 06681 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06682 return -1; 06683 } 06684 q = load_realtime_queue(data); 06685 snprintf(buf, len, "%d", q != NULL? 1 : 0); 06686 if (q) { 06687 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()"); 06688 } 06689 06690 return 0; 06691 }
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 6893 of file app_queue.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), get_member_penalty(), and LOG_ERROR.
06894 { 06895 int penalty; 06896 AST_DECLARE_APP_ARGS(args, 06897 AST_APP_ARG(queuename); 06898 AST_APP_ARG(interface); 06899 ); 06900 /* Make sure the returned value on error is NULL. */ 06901 buf[0] = '\0'; 06902 06903 if (ast_strlen_zero(data)) { 06904 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06905 return -1; 06906 } 06907 06908 AST_STANDARD_APP_ARGS(args, data); 06909 06910 if (args.argc < 2) { 06911 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06912 return -1; 06913 } 06914 06915 penalty = get_member_penalty (args.queuename, args.interface); 06916 06917 if (penalty >= 0) /* remember that buf is already '\0' */ 06918 snprintf (buf, len, "%d", penalty); 06919 06920 return 0; 06921 }
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 6924 of file app_queue.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_ERROR, and set_member_penalty().
06925 { 06926 int penalty; 06927 AST_DECLARE_APP_ARGS(args, 06928 AST_APP_ARG(queuename); 06929 AST_APP_ARG(interface); 06930 ); 06931 06932 if (ast_strlen_zero(data)) { 06933 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06934 return -1; 06935 } 06936 06937 AST_STANDARD_APP_ARGS(args, data); 06938 06939 if (args.argc < 2) { 06940 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06941 return -1; 06942 } 06943 06944 penalty = atoi(value); 06945 06946 if (ast_strlen_zero(args.interface)) { 06947 ast_log (LOG_ERROR, "<interface> parameter can't be null\n"); 06948 return -1; 06949 } 06950 06951 /* if queuename = NULL then penalty will be set for interface in all the queues. */ 06952 if (set_member_penalty(args.queuename, args.interface, penalty)) { 06953 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n"); 06954 return -1; 06955 } 06956 06957 return 0; 06958 }
static int queue_function_qac | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Get number either busy / free / ready or total members of a specific queue.
number | of members (busy / free / ready / total) | |
-1 | on error |
Definition at line 6698 of file app_queue.c.
References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, member::lastcall, load_realtime_queue(), LOG_ERROR, LOG_WARNING, call_queue::members, member::paused, queue_t_unref, member::status, and call_queue::wrapuptime.
06699 { 06700 int count = 0; 06701 struct member *m; 06702 struct ao2_iterator mem_iter; 06703 struct call_queue *q; 06704 char *option; 06705 06706 if (ast_strlen_zero(data)) { 06707 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06708 return -1; 06709 } 06710 06711 if ((option = strchr(data, ','))) 06712 *option++ = '\0'; 06713 else 06714 option = "logged"; 06715 if ((q = load_realtime_queue(data))) { 06716 ao2_lock(q); 06717 if (!strcasecmp(option, "logged")) { 06718 mem_iter = ao2_iterator_init(q->members, 0); 06719 while ((m = ao2_iterator_next(&mem_iter))) { 06720 /* Count the agents who are logged in and presently answering calls */ 06721 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 06722 count++; 06723 } 06724 ao2_ref(m, -1); 06725 } 06726 ao2_iterator_destroy(&mem_iter); 06727 } else if (!strcasecmp(option, "free")) { 06728 mem_iter = ao2_iterator_init(q->members, 0); 06729 while ((m = ao2_iterator_next(&mem_iter))) { 06730 /* Count the agents who are logged in and presently answering calls */ 06731 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) { 06732 count++; 06733 } 06734 ao2_ref(m, -1); 06735 } 06736 ao2_iterator_destroy(&mem_iter); 06737 } else if (!strcasecmp(option, "ready")) { 06738 time_t now; 06739 time(&now); 06740 mem_iter = ao2_iterator_init(q->members, 0); 06741 while ((m = ao2_iterator_next(&mem_iter))) { 06742 /* Count the agents who are logged in, not paused and not wrapping up */ 06743 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) && 06744 !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) { 06745 count++; 06746 } 06747 ao2_ref(m, -1); 06748 } 06749 ao2_iterator_destroy(&mem_iter); 06750 } else /* must be "count" */ 06751 count = ao2_container_count(q->members); 06752 ao2_unlock(q); 06753 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()"); 06754 } else 06755 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06756 06757 snprintf(buf, len, "%d", count); 06758 06759 return 0; 06760 }
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).
number | of members | |
-1 | on error |
Definition at line 6767 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.
06768 { 06769 int count = 0; 06770 struct member *m; 06771 struct call_queue *q; 06772 struct ao2_iterator mem_iter; 06773 static int depflag = 1; 06774 06775 if (depflag) { 06776 depflag = 0; 06777 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"); 06778 } 06779 06780 if (ast_strlen_zero(data)) { 06781 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06782 return -1; 06783 } 06784 06785 if ((q = load_realtime_queue(data))) { 06786 ao2_lock(q); 06787 mem_iter = ao2_iterator_init(q->members, 0); 06788 while ((m = ao2_iterator_next(&mem_iter))) { 06789 /* Count the agents who are logged in and presently answering calls */ 06790 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 06791 count++; 06792 } 06793 ao2_ref(m, -1); 06794 } 06795 ao2_iterator_destroy(&mem_iter); 06796 ao2_unlock(q); 06797 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT"); 06798 } else 06799 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06800 06801 snprintf(buf, len, "%d", count); 06802 06803 return 0; 06804 }
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 6843 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.
06844 { 06845 struct call_queue *q, tmpq = { 06846 .name = data, 06847 }; 06848 struct member *m; 06849 06850 /* Ensure an otherwise empty list doesn't return garbage */ 06851 buf[0] = '\0'; 06852 06853 if (ast_strlen_zero(data)) { 06854 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 06855 return -1; 06856 } 06857 06858 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) { 06859 int buflen = 0, count = 0; 06860 struct ao2_iterator mem_iter; 06861 06862 ao2_lock(q); 06863 mem_iter = ao2_iterator_init(q->members, 0); 06864 while ((m = ao2_iterator_next(&mem_iter))) { 06865 /* strcat() is always faster than printf() */ 06866 if (count++) { 06867 strncat(buf + buflen, ",", len - buflen - 1); 06868 buflen++; 06869 } 06870 strncat(buf + buflen, m->interface, len - buflen - 1); 06871 buflen += strlen(m->interface); 06872 /* Safeguard against overflow (negative length) */ 06873 if (buflen >= len - 2) { 06874 ao2_ref(m, -1); 06875 ast_log(LOG_WARNING, "Truncating list\n"); 06876 break; 06877 } 06878 ao2_ref(m, -1); 06879 } 06880 ao2_iterator_destroy(&mem_iter); 06881 ao2_unlock(q); 06882 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()"); 06883 } else 06884 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06885 06886 /* We should already be terminated, but let's make sure. */ 06887 buf[len - 1] = '\0'; 06888 06889 return 0; 06890 }
static int queue_function_queuememberpaused | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 7019 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, args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, member::membername, call_queue::members, member::paused, queue_t_unref, and queues.
07020 { 07021 struct ast_module_user *lu; 07022 struct call_queue *q; 07023 struct member *cur; 07024 struct ao2_iterator queue_iter; 07025 struct ao2_iterator mem_iter; 07026 char tmp[128] = ""; 07027 char *buffer; 07028 07029 AST_DECLARE_APP_ARGS(args, 07030 AST_APP_ARG(queue); 07031 AST_APP_ARG(interface); 07032 ); 07033 07034 AST_STANDARD_APP_ARGS(args, data); 07035 if (ast_strlen_zero(args.interface)) { 07036 ast_log(LOG_WARNING, "This function requires an interface name.\n"); 07037 return -1; 07038 } 07039 lu = ast_module_user_add(chan); 07040 07041 buffer = ast_malloc(len); 07042 buffer[0]='\0'; 07043 07044 queue_iter = ao2_iterator_init(queues,0); 07045 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07046 ao2_lock(q); 07047 if (ast_strlen_zero(args.queue) || 07048 (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue))) 07049 ) { 07050 /* Iterate over queue members */ 07051 mem_iter = ao2_iterator_init(q->members, 0); 07052 while ((cur = ao2_iterator_next(&mem_iter))) { 07053 if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) { 07054 if (!ast_strlen_zero(args.queue)) { 07055 ast_copy_string(buffer, cur->paused?"1":"0", len); 07056 } else { 07057 snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, cur->paused?"1":"0"); 07058 strncat(buffer, tmp, sizeof(tmp)); 07059 } 07060 ao2_ref(cur, -1); 07061 break; 07062 } 07063 ao2_ref(cur, -1); 07064 } 07065 ao2_iterator_destroy(&mem_iter); 07066 } 07067 ao2_unlock(q); 07068 queue_t_unref(q, "Done with iterator"); 07069 } 07070 ao2_iterator_destroy(&queue_iter); 07071 ast_copy_string(buf, buffer, len); 07072 ast_free(buffer); 07073 07074 ast_module_user_remove(lu); 07075 return 0; 07076 }
static int queue_function_queuememberstatus | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 6960 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, args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_devstate2str(), ast_free, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, member::membername, call_queue::members, queue_t_unref, queues, and member::status.
06961 { 06962 struct ast_module_user *lu; 06963 struct call_queue *q; 06964 struct member *cur; 06965 struct ao2_iterator queue_iter; 06966 struct ao2_iterator mem_iter; 06967 char tmp[128] = ""; 06968 char *buffer; 06969 06970 AST_DECLARE_APP_ARGS(args, 06971 AST_APP_ARG(queue); 06972 AST_APP_ARG(interface); 06973 ); 06974 06975 AST_STANDARD_APP_ARGS(args, data); 06976 if (ast_strlen_zero(args.interface)) { 06977 ast_log(LOG_WARNING, "This function requires an interface name.\n"); 06978 return -1; 06979 } 06980 lu = ast_module_user_add(chan); 06981 06982 buffer = ast_malloc(len); 06983 buffer[0]='\0'; 06984 06985 queue_iter = ao2_iterator_init(queues,0); 06986 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06987 ao2_lock(q); 06988 if (ast_strlen_zero(args.queue) || 06989 (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue))) 06990 ) { 06991 /* Iterate over queue members */ 06992 mem_iter = ao2_iterator_init(q->members, 0); 06993 while ((cur = ao2_iterator_next(&mem_iter))) { 06994 if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) { 06995 if (!ast_strlen_zero(args.queue)) { 06996 ast_copy_string(buffer, ast_devstate2str(cur->status), len); 06997 } else { 06998 snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, ast_devstate2str(cur->status)); 06999 strncat(buffer, tmp, sizeof(tmp)); 07000 } 07001 ao2_ref(cur, -1); 07002 continue; 07003 } 07004 ao2_ref(cur, -1); 07005 } 07006 ao2_iterator_destroy(&mem_iter); 07007 } 07008 ao2_unlock(q); 07009 queue_t_unref(q, "Done with iterator"); 07010 } 07011 ao2_iterator_destroy(&queue_iter); 07012 ast_copy_string(buf, buffer, len); 07013 ast_free(buffer); 07014 07015 ast_module_user_remove(lu); 07016 return 0; 07017 }
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 6807 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.
06808 { 06809 int count = 0; 06810 struct call_queue *q, tmpq = { 06811 .name = data, 06812 }; 06813 struct ast_variable *var = NULL; 06814 06815 buf[0] = '\0'; 06816 06817 if (ast_strlen_zero(data)) { 06818 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n"); 06819 return -1; 06820 } 06821 06822 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) { 06823 ao2_lock(q); 06824 count = q->count; 06825 ao2_unlock(q); 06826 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()"); 06827 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) { 06828 /* if the queue is realtime but was not found in memory, this 06829 * means that the queue had been deleted from memory since it was 06830 * "dead." This means it has a 0 waiting count 06831 */ 06832 count = 0; 06833 ast_variables_destroy(var); 06834 } else 06835 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06836 06837 snprintf(buf, len, "%d", count); 06838 06839 return 0; 06840 }
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.
0 | on success | |
-1 | on error |
Definition at line 6627 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, call_queue::strategy, and call_queue::talktime.
06628 { 06629 int res = -1; 06630 struct call_queue *q, tmpq = { 06631 .name = data, 06632 }; 06633 06634 char interfacevar[256] = ""; 06635 float sl = 0; 06636 06637 if (ast_strlen_zero(data)) { 06638 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06639 return -1; 06640 } 06641 06642 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) { 06643 ao2_lock(q); 06644 if (q->setqueuevar) { 06645 sl = 0; 06646 res = 0; 06647 06648 if (q->callscompleted > 0) { 06649 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 06650 } 06651 06652 snprintf(interfacevar, sizeof(interfacevar), 06653 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 06654 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 06655 06656 pbx_builtin_setvar_multiple(chan, interfacevar); 06657 } 06658 06659 ao2_unlock(q); 06660 queue_t_unref(q, "Done with QUEUE() function"); 06661 } else { 06662 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06663 } 06664 06665 snprintf(buf, len, "%d", res); 06666 06667 return 0; 06668 }
static int queue_hash_cb | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1393 of file app_queue.c.
References ast_str_case_hash().
Referenced by load_module().
01394 { 01395 const struct call_queue *q = obj; 01396 01397 return ast_str_case_hash(q->name); 01398 }
static int queue_member_decrement_followers | ( | void * | obj, | |
void * | arg, | |||
int | flag | |||
) | [static] |
Definition at line 1411 of file app_queue.c.
References member::queuepos.
Referenced by queue_delme_members_decrement_followers(), and queue_member_follower_removal().
static void queue_member_follower_removal | ( | struct call_queue * | queue, | |
struct member * | mem | |||
) | [static] |
Definition at line 1447 of file app_queue.c.
References ao2_callback, call_queue::members, OBJ_MULTIPLE, OBJ_NODATA, queue_ent::pos, queue_member_decrement_followers(), member::queuepos, and call_queue::rrpos.
Referenced by member_remove_from_queue().
01448 { 01449 int pos = mem->queuepos; 01450 01451 /* If the position being removed is less than the current place in the queue, reduce the queue position by one so that we don't skip the member 01452 * who would have been next otherwise. */ 01453 if (pos < queue->rrpos) { 01454 queue->rrpos--; 01455 } 01456 01457 ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &pos); 01458 }
static struct call_queue* queue_ref | ( | struct call_queue * | q | ) | [static, read] |
Definition at line 1472 of file app_queue.c.
References ao2_ref.
Referenced by insert_entry().
01473 { 01474 ao2_ref(q, 1); 01475 return q; 01476 }
static void queue_set_global_params | ( | struct ast_config * | cfg | ) | [static] |
Set the global queue parameters as defined in the "general" section of queues.conf
Definition at line 7179 of file app_queue.c.
References ast_true(), and ast_variable_retrieve().
Referenced by reload_queues().
07180 { 07181 const char *general_val = NULL; 07182 queue_debug = 0; 07183 if ((general_val = ast_variable_retrieve(cfg, "general", "debug"))) 07184 queue_debug = ast_true(general_val); 07185 queue_persistent_members = 0; 07186 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 07187 queue_persistent_members = ast_true(general_val); 07188 autofill_default = 0; 07189 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 07190 autofill_default = ast_true(general_val); 07191 montype_default = 0; 07192 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) { 07193 if (!strcasecmp(general_val, "mixmonitor")) 07194 montype_default = 1; 07195 } 07196 update_cdr = 0; 07197 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) 07198 update_cdr = ast_true(general_val); 07199 shared_lastcall = 0; 07200 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) 07201 shared_lastcall = ast_true(general_val); 07202 }
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.
Definition at line 2089 of file app_queue.c.
References queue_ent::announce, call_queue::announce_to_first_user, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, autopause2int(), queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, queue_ent::moh, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, parse_empty_options(), call_queue::penaltymemberslimit, call_queue::periodicannouncefrequency, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, call_queue::randomperiodicannounce, call_queue::relativeperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, strat2int(), call_queue::strategy, 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_single_queue().
02090 { 02091 if (!strcasecmp(param, "musicclass") || 02092 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 02093 ast_string_field_set(q, moh, val); 02094 } else if (!strcasecmp(param, "announce")) { 02095 ast_string_field_set(q, announce, val); 02096 } else if (!strcasecmp(param, "context")) { 02097 ast_string_field_set(q, context, val); 02098 } else if (!strcasecmp(param, "timeout")) { 02099 q->timeout = atoi(val); 02100 if (q->timeout < 0) 02101 q->timeout = DEFAULT_TIMEOUT; 02102 } else if (!strcasecmp(param, "ringinuse")) { 02103 q->ringinuse = ast_true(val); 02104 } else if (!strcasecmp(param, "setinterfacevar")) { 02105 q->setinterfacevar = ast_true(val); 02106 } else if (!strcasecmp(param, "setqueuevar")) { 02107 q->setqueuevar = ast_true(val); 02108 } else if (!strcasecmp(param, "setqueueentryvar")) { 02109 q->setqueueentryvar = ast_true(val); 02110 } else if (!strcasecmp(param, "monitor-format")) { 02111 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 02112 } else if (!strcasecmp(param, "membermacro")) { 02113 ast_string_field_set(q, membermacro, val); 02114 } else if (!strcasecmp(param, "membergosub")) { 02115 ast_string_field_set(q, membergosub, val); 02116 } else if (!strcasecmp(param, "queue-youarenext")) { 02117 ast_string_field_set(q, sound_next, val); 02118 } else if (!strcasecmp(param, "queue-thereare")) { 02119 ast_string_field_set(q, sound_thereare, val); 02120 } else if (!strcasecmp(param, "queue-callswaiting")) { 02121 ast_string_field_set(q, sound_calls, val); 02122 } else if (!strcasecmp(param, "queue-quantity1")) { 02123 ast_string_field_set(q, queue_quantity1, val); 02124 } else if (!strcasecmp(param, "queue-quantity2")) { 02125 ast_string_field_set(q, queue_quantity2, val); 02126 } else if (!strcasecmp(param, "queue-holdtime")) { 02127 ast_string_field_set(q, sound_holdtime, val); 02128 } else if (!strcasecmp(param, "queue-minutes")) { 02129 ast_string_field_set(q, sound_minutes, val); 02130 } else if (!strcasecmp(param, "queue-minute")) { 02131 ast_string_field_set(q, sound_minute, val); 02132 } else if (!strcasecmp(param, "queue-seconds")) { 02133 ast_string_field_set(q, sound_seconds, val); 02134 } else if (!strcasecmp(param, "queue-thankyou")) { 02135 ast_string_field_set(q, sound_thanks, val); 02136 } else if (!strcasecmp(param, "queue-callerannounce")) { 02137 ast_string_field_set(q, sound_callerannounce, val); 02138 } else if (!strcasecmp(param, "queue-reporthold")) { 02139 ast_string_field_set(q, sound_reporthold, val); 02140 } else if (!strcasecmp(param, "announce-frequency")) { 02141 q->announcefrequency = atoi(val); 02142 } else if (!strcasecmp(param, "announce-to-first-user")) { 02143 q->announce_to_first_user = ast_true(val); 02144 } else if (!strcasecmp(param, "min-announce-frequency")) { 02145 q->minannouncefrequency = atoi(val); 02146 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name); 02147 } else if (!strcasecmp(param, "announce-round-seconds")) { 02148 q->roundingseconds = atoi(val); 02149 /* Rounding to any other values just doesn't make sense... */ 02150 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10 02151 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) { 02152 if (linenum >= 0) { 02153 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 02154 "using 0 instead for queue '%s' at line %d of queues.conf\n", 02155 val, param, q->name, linenum); 02156 } else { 02157 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 02158 "using 0 instead for queue '%s'\n", val, param, q->name); 02159 } 02160 q->roundingseconds=0; 02161 } 02162 } else if (!strcasecmp(param, "announce-holdtime")) { 02163 if (!strcasecmp(val, "once")) 02164 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 02165 else if (ast_true(val)) 02166 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 02167 else 02168 q->announceholdtime = 0; 02169 } else if (!strcasecmp(param, "announce-position")) { 02170 if (!strcasecmp(val, "limit")) 02171 q->announceposition = ANNOUNCEPOSITION_LIMIT; 02172 else if (!strcasecmp(val, "more")) 02173 q->announceposition = ANNOUNCEPOSITION_MORE_THAN; 02174 else if (ast_true(val)) 02175 q->announceposition = ANNOUNCEPOSITION_YES; 02176 else 02177 q->announceposition = ANNOUNCEPOSITION_NO; 02178 } else if (!strcasecmp(param, "announce-position-limit")) { 02179 q->announcepositionlimit = atoi(val); 02180 } else if (!strcasecmp(param, "periodic-announce")) { 02181 if (strchr(val, ',')) { 02182 char *s, *buf = ast_strdupa(val); 02183 unsigned int i = 0; 02184 02185 while ((s = strsep(&buf, ",|"))) { 02186 if (!q->sound_periodicannounce[i]) 02187 q->sound_periodicannounce[i] = ast_str_create(16); 02188 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s); 02189 i++; 02190 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 02191 break; 02192 } 02193 q->numperiodicannounce = i; 02194 } else { 02195 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val); 02196 q->numperiodicannounce = 1; 02197 } 02198 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 02199 q->periodicannouncefrequency = atoi(val); 02200 } else if (!strcasecmp(param, "relative-periodic-announce")) { 02201 q->relativeperiodicannounce = ast_true(val); 02202 } else if (!strcasecmp(param, "random-periodic-announce")) { 02203 q->randomperiodicannounce = ast_true(val); 02204 } else if (!strcasecmp(param, "retry")) { 02205 q->retry = atoi(val); 02206 if (q->retry <= 0) 02207 q->retry = DEFAULT_RETRY; 02208 } else if (!strcasecmp(param, "wrapuptime")) { 02209 q->wrapuptime = atoi(val); 02210 } else if (!strcasecmp(param, "penaltymemberslimit")) { 02211 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) { 02212 q->penaltymemberslimit = 0; 02213 } 02214 } else if (!strcasecmp(param, "autofill")) { 02215 q->autofill = ast_true(val); 02216 } else if (!strcasecmp(param, "monitor-type")) { 02217 if (!strcasecmp(val, "mixmonitor")) 02218 q->montype = 1; 02219 } else if (!strcasecmp(param, "autopause")) { 02220 q->autopause = autopause2int(val); 02221 } else if (!strcasecmp(param, "maxlen")) { 02222 q->maxlen = atoi(val); 02223 if (q->maxlen < 0) 02224 q->maxlen = 0; 02225 } else if (!strcasecmp(param, "ringlimit")) { 02226 q->ringlimit = atoi(val); 02227 if (q->ringlimit < 0) 02228 q->ringlimit = 0; 02229 } else if (!strcasecmp(param, "servicelevel")) { 02230 q->servicelevel= atoi(val); 02231 } else if (!strcasecmp(param, "strategy")) { 02232 int strategy; 02233 02234 /* We are a static queue and already have set this, no need to do it again */ 02235 if (failunknown) { 02236 return; 02237 } 02238 strategy = strat2int(val); 02239 if (strategy < 0) { 02240 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 02241 val, q->name); 02242 q->strategy = QUEUE_STRATEGY_RINGALL; 02243 } 02244 if (strategy == q->strategy) { 02245 return; 02246 } 02247 if (strategy == QUEUE_STRATEGY_LINEAR) { 02248 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n"); 02249 return; 02250 } 02251 q->strategy = strategy; 02252 } else if (!strcasecmp(param, "joinempty")) { 02253 parse_empty_options(val, &q->joinempty, 1); 02254 } else if (!strcasecmp(param, "leavewhenempty")) { 02255 parse_empty_options(val, &q->leavewhenempty, 0); 02256 } else if (!strcasecmp(param, "eventmemberstatus")) { 02257 q->maskmemberstatus = !ast_true(val); 02258 } else if (!strcasecmp(param, "eventwhencalled")) { 02259 if (!strcasecmp(val, "vars")) { 02260 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 02261 } else { 02262 q->eventwhencalled = ast_true(val) ? 1 : 0; 02263 } 02264 } else if (!strcasecmp(param, "reportholdtime")) { 02265 q->reportholdtime = ast_true(val); 02266 } else if (!strcasecmp(param, "memberdelay")) { 02267 q->memberdelay = atoi(val); 02268 } else if (!strcasecmp(param, "weight")) { 02269 q->weight = atoi(val); 02270 } else if (!strcasecmp(param, "timeoutrestart")) { 02271 q->timeoutrestart = ast_true(val); 02272 } else if (!strcasecmp(param, "defaultrule")) { 02273 ast_string_field_set(q, defaultrule, val); 02274 } else if (!strcasecmp(param, "timeoutpriority")) { 02275 if (!strcasecmp(val, "conf")) { 02276 q->timeoutpriority = TIMEOUT_PRIORITY_CONF; 02277 } else { 02278 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 02279 } 02280 } else if (failunknown) { 02281 if (linenum >= 0) { 02282 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 02283 q->name, param, linenum); 02284 } else { 02285 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 02286 } 02287 } 02288 }
static char* queue_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7838 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.
07839 { 07840 switch ( cmd ) { 07841 case CLI_INIT: 07842 e->command = "queue show"; 07843 e->usage = 07844 "Usage: queue show\n" 07845 " Provides summary information on a specified queue.\n"; 07846 return NULL; 07847 case CLI_GENERATE: 07848 return complete_queue_show(a->line, a->word, a->pos, a->n); 07849 } 07850 07851 return __queues_show(NULL, a->fd, a->argc, a->argv); 07852 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 4676 of file app_queue.c.
References ast_free.
04677 { 04678 struct queue_transfer_ds *qtds = data; 04679 ast_free(qtds); 04680 }
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 4699 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, queue_ent::opos, queue_ent::parent, queue_transfer_ds::qe, queue_ent::start, queue_transfer_ds::starttime, and update_queue().
04700 { 04701 struct queue_transfer_ds *qtds = data; 04702 struct queue_ent *qe = qtds->qe; 04703 struct member *member = qtds->member; 04704 time_t callstart = qtds->starttime; 04705 int callcompletedinsl = qtds->callcompletedinsl; 04706 struct ast_datastore *datastore; 04707 04708 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 04709 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 04710 (long) (time(NULL) - callstart), qe->opos); 04711 04712 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 04713 04714 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 04715 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 04716 ast_channel_datastore_remove(old_chan, datastore); 04717 /* Datastore is freed in try_calling() */ 04718 } else { 04719 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 04720 } 04721 }
static struct call_queue* queue_unref | ( | struct call_queue * | q | ) | [static, read] |
Definition at line 1478 of file app_queue.c.
References ao2_ref.
Referenced by queue_exec(), and queues_data_provider_get().
01479 { 01480 ao2_ref(q, -1); 01481 return NULL; 01482 }
static int queues_data_provider_get | ( | const struct ast_data_search * | search, | |
struct ast_data * | data_root | |||
) | [static] |
Definition at line 9008 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_load_realtime_multientry(), ast_strlen_zero(), load_realtime_queue(), queue_unref(), queues, queues_data_provider_get_helper(), call_queue::realtime, and SENTINEL.
09010 { 09011 struct ao2_iterator i; 09012 struct call_queue *queue, *queue_realtime = NULL; 09013 struct ast_config *cfg; 09014 char *queuename; 09015 09016 /* load realtime queues. */ 09017 cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 09018 if (cfg) { 09019 for (queuename = ast_category_browse(cfg, NULL); 09020 !ast_strlen_zero(queuename); 09021 queuename = ast_category_browse(cfg, queuename)) { 09022 if ((queue = load_realtime_queue(queuename))) { 09023 queue_unref(queue); 09024 } 09025 } 09026 ast_config_destroy(cfg); 09027 } 09028 09029 /* static queues. */ 09030 i = ao2_iterator_init(queues, 0); 09031 while ((queue = ao2_iterator_next(&i))) { 09032 ao2_lock(queue); 09033 if (queue->realtime) { 09034 queue_realtime = load_realtime_queue(queue->name); 09035 if (!queue_realtime) { 09036 ao2_unlock(queue); 09037 queue_unref(queue); 09038 continue; 09039 } 09040 queue_unref(queue_realtime); 09041 } 09042 09043 queues_data_provider_get_helper(search, data_root, queue); 09044 ao2_unlock(queue); 09045 queue_unref(queue); 09046 } 09047 ao2_iterator_destroy(&i); 09048 09049 return 0; 09050 }
static void queues_data_provider_get_helper | ( | const struct ast_data_search * | search, | |
struct ast_data * | data_root, | |||
struct call_queue * | queue | |||
) | [static] |
Definition at line 8902 of file app_queue.c.
References call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_channel_data_add_structure(), ast_data_add_int(), ast_data_add_node(), ast_data_add_str(), ast_data_add_structure, ast_data_remove_node(), ast_data_search_match(), queue_ent::chan, call_queue::head, int2strat(), call_queue::members, and call_queue::strategy.
Referenced by queues_data_provider_get().
08904 { 08905 struct ao2_iterator im; 08906 struct member *member; 08907 struct queue_ent *qe; 08908 struct ast_data *data_queue, *data_members = NULL, *enum_node; 08909 struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel; 08910 08911 data_queue = ast_data_add_node(data_root, "queue"); 08912 if (!data_queue) { 08913 return; 08914 } 08915 08916 ast_data_add_structure(call_queue, data_queue, queue); 08917 08918 ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy)); 08919 ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members)); 08920 08921 /* announce position */ 08922 enum_node = ast_data_add_node(data_queue, "announceposition"); 08923 if (!enum_node) { 08924 return; 08925 } 08926 switch (queue->announceposition) { 08927 case ANNOUNCEPOSITION_LIMIT: 08928 ast_data_add_str(enum_node, "text", "limit"); 08929 break; 08930 case ANNOUNCEPOSITION_MORE_THAN: 08931 ast_data_add_str(enum_node, "text", "more"); 08932 break; 08933 case ANNOUNCEPOSITION_YES: 08934 ast_data_add_str(enum_node, "text", "yes"); 08935 break; 08936 case ANNOUNCEPOSITION_NO: 08937 ast_data_add_str(enum_node, "text", "no"); 08938 break; 08939 default: 08940 ast_data_add_str(enum_node, "text", "unknown"); 08941 break; 08942 } 08943 ast_data_add_int(enum_node, "value", queue->announceposition); 08944 08945 /* add queue members */ 08946 im = ao2_iterator_init(queue->members, 0); 08947 while ((member = ao2_iterator_next(&im))) { 08948 if (!data_members) { 08949 data_members = ast_data_add_node(data_queue, "members"); 08950 if (!data_members) { 08951 ao2_ref(member, -1); 08952 continue; 08953 } 08954 } 08955 08956 data_member = ast_data_add_node(data_members, "member"); 08957 if (!data_member) { 08958 ao2_ref(member, -1); 08959 continue; 08960 } 08961 08962 ast_data_add_structure(member, data_member, member); 08963 08964 ao2_ref(member, -1); 08965 } 08966 ao2_iterator_destroy(&im); 08967 08968 /* include the callers inside the result. */ 08969 if (queue->head) { 08970 for (qe = queue->head; qe; qe = qe->next) { 08971 if (!data_callers) { 08972 data_callers = ast_data_add_node(data_queue, "callers"); 08973 if (!data_callers) { 08974 continue; 08975 } 08976 } 08977 08978 data_caller = ast_data_add_node(data_callers, "caller"); 08979 if (!data_caller) { 08980 continue; 08981 } 08982 08983 ast_data_add_structure(queue_ent, data_caller, qe); 08984 08985 /* add the caller channel. */ 08986 data_caller_channel = ast_data_add_node(data_caller, "channel"); 08987 if (!data_caller_channel) { 08988 continue; 08989 } 08990 08991 ast_channel_data_add_structure(data_caller_channel, qe->chan, 1); 08992 } 08993 } 08994 08995 /* if this queue doesn't match remove the added queue. */ 08996 if (!ast_data_search_match(search, data_queue)) { 08997 ast_data_remove_node(data_root, data_queue); 08998 } 08999 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 2989 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::holdtime, and queue_ent::parent.
Referenced by try_calling().
02990 { 02991 int oldvalue; 02992 02993 /* Calculate holdtime using an exponential average */ 02994 /* Thanks to SRT for this contribution */ 02995 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 02996 02997 ao2_lock(qe->parent); 02998 oldvalue = qe->parent->holdtime; 02999 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 03000 ao2_unlock(qe->parent); 03001 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Record that a caller gave up on waiting in queue.
Definition at line 3702 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, manager_event, queue_ent::opos, queue_ent::parent, queue_ent::pos, set_queue_variables(), and queue_ent::start.
Referenced by queue_exec(), and try_calling().
03703 { 03704 set_queue_variables(qe->parent, qe->chan); 03705 ao2_lock(qe->parent); 03706 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 03707 "Queue: %s\r\n" 03708 "Uniqueid: %s\r\n" 03709 "Position: %d\r\n" 03710 "OriginalPosition: %d\r\n" 03711 "HoldTime: %d\r\n", 03712 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 03713 03714 qe->parent->callsabandoned++; 03715 ao2_unlock(qe->parent); 03716 }
static int reload | ( | void | ) | [static] |
Definition at line 9188 of file app_queue.c.
References AST_FLAGS_ALL, ast_unload_realtime(), QUEUE_RESET_STATS, and reload_handler().
09189 { 09190 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,}; 09191 ast_unload_realtime("queue_members"); 09192 reload_handler(1, &mask, NULL); 09193 return 0; 09194 }
static int reload_handler | ( | int | reload, | |
struct ast_flags * | mask, | |||
const char * | queuename | |||
) | [static] |
The command center for all reload operations.
Whenever any piece of queue information is to be reloaded, this function is called. It interprets the flags set in the mask parameter and acts based on how they are set.
reload | True if we are reloading information, false if we are loading information for the first time. | |
mask | A bitmask which tells the handler what actions to take | |
queuename | The name of the queue on which we wish to take action |
0 | All reloads were successful | |
non-zero | There was a failure |
Definition at line 7558 of file app_queue.c.
References ast_test_flag, clear_stats(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, QUEUE_RESET_STATS, reload_queue_rules(), and reload_queues().
Referenced by handle_queue_reload(), handle_queue_reset(), load_module(), manager_queue_reload(), manager_queue_reset(), and reload().
07559 { 07560 int res = 0; 07561 07562 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) { 07563 res |= reload_queue_rules(reload); 07564 } 07565 if (ast_test_flag(mask, QUEUE_RESET_STATS)) { 07566 res |= clear_stats(queuename); 07567 } 07568 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) { 07569 res |= reload_queues(reload, mask, queuename); 07570 } 07571 return res; 07572 }
static void reload_queue_members | ( | void | ) | [static] |
Reload dynamic queue members persisted into the astdb.
Definition at line 5968 of file app_queue.c.
References add_to_queue(), ao2_t_find, ast_db_del(), ast_db_freetree(), ast_db_get_allocated(), ast_db_gettree(), ast_debug, ast_free, ast_log(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, queue_t_unref, queues, RES_OUTOFMEMORY, and member::state_interface.
Referenced by load_module().
05969 { 05970 char *cur_ptr; 05971 const char *queue_name; 05972 char *member; 05973 char *interface; 05974 char *membername = NULL; 05975 char *state_interface; 05976 char *penalty_tok; 05977 int penalty = 0; 05978 char *paused_tok; 05979 int paused = 0; 05980 struct ast_db_entry *db_tree; 05981 struct ast_db_entry *entry; 05982 struct call_queue *cur_queue; 05983 char *queue_data; 05984 05985 /* Each key in 'pm_family' is the name of a queue */ 05986 db_tree = ast_db_gettree(pm_family, NULL); 05987 for (entry = db_tree; entry; entry = entry->next) { 05988 05989 queue_name = entry->key + strlen(pm_family) + 2; 05990 05991 { 05992 struct call_queue tmpq = { 05993 .name = queue_name, 05994 }; 05995 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members"); 05996 } 05997 05998 if (!cur_queue) 05999 cur_queue = load_realtime_queue(queue_name); 06000 06001 if (!cur_queue) { 06002 /* If the queue no longer exists, remove it from the 06003 * database */ 06004 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 06005 ast_db_del(pm_family, queue_name); 06006 continue; 06007 } 06008 06009 if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) { 06010 queue_t_unref(cur_queue, "Expire reload reference"); 06011 continue; 06012 } 06013 06014 cur_ptr = queue_data; 06015 while ((member = strsep(&cur_ptr, ",|"))) { 06016 if (ast_strlen_zero(member)) 06017 continue; 06018 06019 interface = strsep(&member, ";"); 06020 penalty_tok = strsep(&member, ";"); 06021 paused_tok = strsep(&member, ";"); 06022 membername = strsep(&member, ";"); 06023 state_interface = strsep(&member, ";"); 06024 06025 if (!penalty_tok) { 06026 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 06027 break; 06028 } 06029 penalty = strtol(penalty_tok, NULL, 10); 06030 if (errno == ERANGE) { 06031 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 06032 break; 06033 } 06034 06035 if (!paused_tok) { 06036 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 06037 break; 06038 } 06039 paused = strtol(paused_tok, NULL, 10); 06040 if ((errno == ERANGE) || paused < 0 || paused > 1) { 06041 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 06042 break; 06043 } 06044 06045 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 06046 06047 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) { 06048 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 06049 break; 06050 } 06051 } 06052 queue_t_unref(cur_queue, "Expire reload reference"); 06053 ast_free(queue_data); 06054 } 06055 06056 if (db_tree) { 06057 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 06058 ast_db_freetree(db_tree); 06059 } 06060 }
static int reload_queue_rules | ( | int | reload | ) | [static] |
Reload the rules defined in queuerules.conf.
reload | If 1, then only process queuerules.conf if the file has changed since the last time we inspected it. |
Definition at line 7130 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_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, rule_list::name, ast_variable::next, and ast_variable::value.
Referenced by reload_handler().
07131 { 07132 struct ast_config *cfg; 07133 struct rule_list *rl_iter, *new_rl; 07134 struct penalty_rule *pr_iter; 07135 char *rulecat = NULL; 07136 struct ast_variable *rulevar = NULL; 07137 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 07138 07139 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) { 07140 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n"); 07141 return AST_MODULE_LOAD_SUCCESS; 07142 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 07143 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n"); 07144 return AST_MODULE_LOAD_SUCCESS; 07145 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 07146 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n"); 07147 return AST_MODULE_LOAD_SUCCESS; 07148 } 07149 07150 AST_LIST_LOCK(&rule_lists); 07151 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) { 07152 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list))) 07153 ast_free(pr_iter); 07154 ast_free(rl_iter); 07155 } 07156 while ((rulecat = ast_category_browse(cfg, rulecat))) { 07157 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) { 07158 AST_LIST_UNLOCK(&rule_lists); 07159 ast_config_destroy(cfg); 07160 return AST_MODULE_LOAD_FAILURE; 07161 } else { 07162 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name)); 07163 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list); 07164 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next) 07165 if(!strcasecmp(rulevar->name, "penaltychange")) 07166 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno); 07167 else 07168 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno); 07169 } 07170 } 07171 AST_LIST_UNLOCK(&rule_lists); 07172 07173 ast_config_destroy(cfg); 07174 07175 return AST_MODULE_LOAD_SUCCESS; 07176 }
static int reload_queues | ( | int | reload, | |
struct ast_flags * | mask, | |||
const char * | queuename | |||
) | [static] |
reload the queues.conf file
This function reloads the information in the general section of the queues.conf file and potentially more, depending on the value of mask.
reload | 0 if we are calling this the first time, 1 every other time | |
mask | Gives flags telling us what information to actually reload | |
queuename | If set to a non-zero string, then only reload information from that particular queue. Otherwise inspect all queues |
-1 | Failure occurred | |
0 | All clear! |
Definition at line 7463 of file app_queue.c.
References ao2_callback, ao2_lock, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_log(), ast_strlen_zero(), ast_test_flag, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, kill_dead_queues(), LOG_ERROR, LOG_NOTICE, mark_dead_and_unfound(), OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_global_params(), queues, reload_single_queue(), and remove_members_and_mark_unfound().
Referenced by reload_handler().
07464 { 07465 struct ast_config *cfg; 07466 char *cat; 07467 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 07468 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 07469 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER); 07470 07471 if (!(cfg = ast_config_load("queues.conf", config_flags))) { 07472 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 07473 return -1; 07474 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 07475 return 0; 07476 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 07477 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n"); 07478 return -1; 07479 } 07480 07481 /* We've made it here, so it looks like we're doing operations on all queues. */ 07482 ao2_lock(queues); 07483 07484 /* Mark all queues as dead for the moment if we're reloading queues. 07485 * For clarity, we could just be reloading members, in which case we don't want to mess 07486 * with the other queue parameters at all*/ 07487 if (queue_reload) { 07488 ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename); 07489 } 07490 07491 if (member_reload) { 07492 ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename); 07493 } 07494 07495 /* Chug through config file */ 07496 cat = NULL; 07497 while ((cat = ast_category_browse(cfg, cat)) ) { 07498 if (!strcasecmp(cat, "general") && queue_reload) { 07499 queue_set_global_params(cfg); 07500 continue; 07501 } 07502 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename)) 07503 reload_single_queue(cfg, mask, cat); 07504 } 07505 07506 ast_config_destroy(cfg); 07507 /* Unref all the dead queues if we were reloading queues */ 07508 if (queue_reload) { 07509 ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename); 07510 } 07511 ao2_unlock(queues); 07512 return 0; 07513 }
static void reload_single_member | ( | const char * | memberdata, | |
struct call_queue * | q | |||
) | [static] |
reload information pertaining to a single member
This function is called when a member = line is encountered in queues.conf.
memberdata | The part after member = in the config file | |
q | The queue to which this member belongs |
Definition at line 7212 of file app_queue.c.
References ao2_find, ao2_link, ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, args, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strip(), ast_strlen_zero(), create_queue_member(), member::interface, LOG_WARNING, member_add_to_queue(), call_queue::members, OBJ_POINTER, parse(), member::paused, member::penalty, and member::queuepos.
Referenced by reload_single_queue().
07213 { 07214 char *membername, *interface, *state_interface, *tmp; 07215 char *parse; 07216 struct member *cur, *newm; 07217 struct member tmpmem; 07218 int penalty; 07219 AST_DECLARE_APP_ARGS(args, 07220 AST_APP_ARG(interface); 07221 AST_APP_ARG(penalty); 07222 AST_APP_ARG(membername); 07223 AST_APP_ARG(state_interface); 07224 ); 07225 07226 if (ast_strlen_zero(memberdata)) { 07227 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n"); 07228 return; 07229 } 07230 07231 /* Add a new member */ 07232 parse = ast_strdupa(memberdata); 07233 07234 AST_STANDARD_APP_ARGS(args, parse); 07235 07236 interface = args.interface; 07237 if (!ast_strlen_zero(args.penalty)) { 07238 tmp = args.penalty; 07239 ast_strip(tmp); 07240 penalty = atoi(tmp); 07241 if (penalty < 0) { 07242 penalty = 0; 07243 } 07244 } else { 07245 penalty = 0; 07246 } 07247 07248 if (!ast_strlen_zero(args.membername)) { 07249 membername = args.membername; 07250 ast_strip(membername); 07251 } else { 07252 membername = interface; 07253 } 07254 07255 if (!ast_strlen_zero(args.state_interface)) { 07256 state_interface = args.state_interface; 07257 ast_strip(state_interface); 07258 } else { 07259 state_interface = interface; 07260 } 07261 07262 /* Find the old position in the list */ 07263 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 07264 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER); 07265 07266 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) { 07267 if (cur) { 07268 /* Round Robin Queue Position must be copied if this is replacing an existing member */ 07269 ao2_lock(q->members); 07270 newm->queuepos = cur->queuepos; 07271 ao2_link(q->members, newm); 07272 ao2_unlink(q->members, cur); 07273 ao2_unlock(q->members); 07274 } else { 07275 /* Otherwise we need to add using the function that will apply a round robin queue position manually. */ 07276 member_add_to_queue(q, newm); 07277 } 07278 ao2_ref(newm, -1); 07279 } 07280 newm = NULL; 07281 07282 if (cur) { 07283 ao2_ref(cur, -1); 07284 } 07285 }
static void reload_single_queue | ( | struct ast_config * | cfg, | |
struct ast_flags * | mask, | |||
const char * | queuename | |||
) | [static] |
Reload information pertaining to a particular queue.
Once we have isolated a queue within reload_queues, we call this. This will either reload information for the queue or if we're just reloading member information, we'll just reload that without touching other settings within the queue
cfg | The configuration which we are reading | |
mask | Tells us what information we need to reload | |
queuename | The name of the queue we are reloading information from |
void |
Definition at line 7319 of file app_queue.c.
References alloc_queue(), ao2_callback, ao2_lock, ao2_t_find, ao2_unlock, ast_atomic_fetchadd_int(), ast_log(), ast_test_flag, ast_variable_browse(), ast_variable_retrieve(), call_queue::found, init_queue(), kill_dead_members(), ast_variable::lineno, LOG_WARNING, mark_member_dead(), call_queue::members, ast_variable::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_POINTER, OBJ_UNLINK, queue_delme_members_decrement_followers(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_link, reload_single_member(), strat2int(), call_queue::strategy, ast_variable::value, var, and call_queue::weight.
Referenced by reload_queues().
07320 { 07321 int new; 07322 struct call_queue *q = NULL; 07323 /*We're defining a queue*/ 07324 struct call_queue tmpq = { 07325 .name = queuename, 07326 }; 07327 const char *tmpvar; 07328 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 07329 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER); 07330 int prev_weight = 0; 07331 struct ast_variable *var; 07332 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) { 07333 if (queue_reload) { 07334 /* Make one then */ 07335 if (!(q = alloc_queue(queuename))) { 07336 return; 07337 } 07338 } else { 07339 /* Since we're not reloading queues, this means that we found a queue 07340 * in the configuration file which we don't know about yet. Just return. 07341 */ 07342 return; 07343 } 07344 new = 1; 07345 } else { 07346 new = 0; 07347 } 07348 07349 if (!new) { 07350 ao2_lock(q); 07351 prev_weight = q->weight ? 1 : 0; 07352 } 07353 /* Check if we already found a queue with this name in the config file */ 07354 if (q->found) { 07355 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename); 07356 if (!new) { 07357 /* It should be impossible to *not* hit this case*/ 07358 ao2_unlock(q); 07359 } 07360 queue_t_unref(q, "We exist! Expiring temporary pointer"); 07361 return; 07362 } 07363 /* Due to the fact that the "linear" strategy will have a different allocation 07364 * scheme for queue members, we must devise the queue's strategy before other initializations. 07365 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2 07366 * container used will have only a single bucket instead of the typical number. 07367 */ 07368 if (queue_reload) { 07369 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) { 07370 q->strategy = strat2int(tmpvar); 07371 if (q->strategy < 0) { 07372 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 07373 tmpvar, q->name); 07374 q->strategy = QUEUE_STRATEGY_RINGALL; 07375 } 07376 } else { 07377 q->strategy = QUEUE_STRATEGY_RINGALL; 07378 } 07379 init_queue(q); 07380 } 07381 if (member_reload) { 07382 ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL); 07383 q->found = 1; 07384 } 07385 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) { 07386 if (member_reload && !strcasecmp(var->name, "member")) { 07387 reload_single_member(var->value, q); 07388 } else if (queue_reload) { 07389 queue_set_param(q, var->name, var->value, var->lineno, 1); 07390 } 07391 } 07392 /* At this point, we've determined if the queue has a weight, so update use_weight 07393 * as appropriate 07394 */ 07395 if (!q->weight && prev_weight) { 07396 ast_atomic_fetchadd_int(&use_weight, -1); 07397 } 07398 else if (q->weight && !prev_weight) { 07399 ast_atomic_fetchadd_int(&use_weight, +1); 07400 } 07401 07402 /* Free remaining members marked as delme */ 07403 if (member_reload) { 07404 ao2_lock(q->members); 07405 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q); 07406 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q); 07407 ao2_unlock(q->members); 07408 } 07409 07410 if (new) { 07411 queues_t_link(queues, q, "Add queue to container"); 07412 } else { 07413 ao2_unlock(q); 07414 } 07415 queue_t_unref(q, "Expiring creation reference"); 07416 }
static int remove_from_queue | ( | const char * | queuename, | |
const char * | interface | |||
) | [static] |
Remove member from queue.
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 5711 of file app_queue.c.
References ao2_find, ao2_lock, ao2_ref, ao2_t_find, ao2_unlock, ast_copy_string(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event, member_remove_from_queue(), member::membername, call_queue::members, OBJ_POINTER, queue_t_unref, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.
Referenced by handle_queue_remove_member(), manager_remove_queue_member(), and rqm_exec().
05712 { 05713 struct call_queue *q, tmpq = { 05714 .name = queuename, 05715 }; 05716 struct member *mem, tmpmem; 05717 int res = RES_NOSUCHQUEUE; 05718 05719 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 05720 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) { 05721 ao2_lock(q); 05722 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 05723 /* XXX future changes should beware of this assumption!! */ 05724 if (!mem->dynamic) { 05725 ao2_ref(mem, -1); 05726 ao2_unlock(q); 05727 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference"); 05728 return RES_NOT_DYNAMIC; 05729 } 05730 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 05731 "Queue: %s\r\n" 05732 "Location: %s\r\n" 05733 "MemberName: %s\r\n", 05734 q->name, mem->interface, mem->membername); 05735 member_remove_from_queue(q, mem); 05736 ao2_ref(mem, -1); 05737 05738 if (queue_persistent_members) 05739 dump_queue_members(q); 05740 05741 res = RES_OKAY; 05742 } else { 05743 res = RES_EXISTS; 05744 } 05745 ao2_unlock(q); 05746 queue_t_unref(q, "Expiring temporary reference"); 05747 } 05748 05749 return res; 05750 }
static int remove_members_and_mark_unfound | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7418 of file app_queue.c.
References ast_strlen_zero(), call_queue::found, and call_queue::realtime.
Referenced by reload_queues().
07419 { 07420 struct call_queue *q = obj; 07421 char *queuename = arg; 07422 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) { 07423 q->found = 0; 07424 07425 } 07426 return 0; 07427 }
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:
1 | on success to reach a free agent | |
0 | on failure to get agent. |
Definition at line 3364 of file app_queue.c.
References ast_cdr::accountcode, ast_channel::adsicpe, ast_cdr::amaflags, ast_party_connected_line::ani, ast_party_caller::ani, ao2_lock, ao2_unlock, ast_channel::appl, ast_assert, ast_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock_both, ast_channel_set_caller_event(), ast_channel_unlock, ast_connected_line_copy_from_caller(), ast_copy_string(), AST_FLAG_ANSWERED_ELSEWHERE, ast_log(), ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_set_flag, ast_string_field_set, ast_strlen_zero(), ast_verb, member::call_pending, ast_channel::caller, can_ring_entry(), queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_cdr::clid, ast_channel::connected, ast_channel::context, ast_channel::data, ast_cdr::dcontext, callattempt::dial_callerid_absent, dialcontext, ast_channel::dialed, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, ast_party_connected_line::id, ast_party_caller::id, callattempt::interface, ast_cdr::lastapp, ast_cdr::lastdata, queue_ent::linpos, LOG_DEBUG, LOG_NOTICE, ast_channel::macroexten, manager_event, callattempt::member, member_call_pending_clear(), member::membername, ast_party_id::name, ast_channel::nativeformats, ast_party_dialed::number, ast_party_id::number, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, ast_channel::redirecting, call_queue::ringinuse, call_queue::rrpos, S_COR, S_OR, ast_cdr::src, status, callattempt::stillgoing, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_party_dialed::transit_network_select, ast_cdr::userfield, ast_party_name::valid, ast_party_number::valid, vars2manager(), and ast_channel::whentohangup.
Referenced by ring_one().
03365 { 03366 int res; 03367 int status; 03368 char tech[256]; 03369 char *location, *location2; 03370 const char *macrocontext, *macroexten; 03371 char pickupmark[256], chan[128]; 03372 03373 /* on entry here, we know that tmp->chan == NULL */ 03374 if (!can_ring_entry(qe, tmp)) { 03375 tmp->stillgoing = 0; 03376 (*busies)++; 03377 return 0; 03378 } 03379 ast_assert(qe->parent->ringinuse || tmp->member->call_pending); 03380 03381 ast_copy_string(tech, tmp->interface, sizeof(tech)); 03382 if ((location = strchr(tech, '/'))) 03383 *location++ = '\0'; 03384 else 03385 location = ""; 03386 03387 /* Request the peer */ 03388 tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status); 03389 if (!tmp->chan) { /* If we can't, just go on to the next call */ 03390 if (queue_debug) 03391 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech); 03392 ao2_lock(qe->parent); 03393 qe->parent->rrpos++; 03394 qe->linpos++; 03395 ao2_unlock(qe->parent); 03396 03397 member_call_pending_clear(tmp->member); 03398 03399 if (qe->chan->cdr) { 03400 ast_cdr_busy(qe->chan->cdr); 03401 } 03402 tmp->stillgoing = 0; 03403 (*busies)++; 03404 return 0; 03405 } 03406 03407 ast_channel_lock_both(tmp->chan, qe->chan); 03408 03409 if (qe->cancel_answered_elsewhere) { 03410 ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); 03411 } 03412 tmp->chan->appl = "AppQueue"; 03413 tmp->chan->data = "(Outgoing Line)"; 03414 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); 03415 03416 /* If the new channel has no callerid, try to guess what it should be */ 03417 if (!tmp->chan->caller.id.number.valid) { 03418 if (qe->chan->connected.id.number.valid) { 03419 struct ast_party_caller caller; 03420 03421 ast_party_caller_set_init(&caller, &tmp->chan->caller); 03422 caller.id = qe->chan->connected.id; 03423 caller.ani = qe->chan->connected.ani; 03424 ast_channel_set_caller_event(tmp->chan, &caller, NULL); 03425 } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) { 03426 ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL); 03427 } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) { 03428 ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 03429 } 03430 tmp->dial_callerid_absent = 1; 03431 } 03432 03433 ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting); 03434 03435 tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select; 03436 03437 ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller); 03438 03439 /* Inherit specially named variables from parent channel */ 03440 ast_channel_inherit_variables(qe->chan, tmp->chan); 03441 ast_channel_datastore_inherit(qe->chan, tmp->chan); 03442 03443 /* Presense of ADSI CPE on outgoing channel follows ours */ 03444 tmp->chan->adsicpe = qe->chan->adsicpe; 03445 03446 /* Inherit context and extension */ 03447 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 03448 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); 03449 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 03450 if (!ast_strlen_zero(macroexten)) 03451 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 03452 else 03453 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 03454 if (ast_cdr_isset_unanswered()) { 03455 /* they want to see the unanswered dial attempts! */ 03456 /* set up the CDR fields on all the CDRs to give sensical information */ 03457 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name); 03458 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid); 03459 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel); 03460 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src); 03461 strcpy(tmp->chan->cdr->dst, qe->chan->exten); 03462 strcpy(tmp->chan->cdr->dcontext, qe->chan->context); 03463 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp); 03464 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata); 03465 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags; 03466 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode); 03467 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield); 03468 } 03469 03470 ast_channel_unlock(tmp->chan); 03471 ast_channel_unlock(qe->chan); 03472 03473 /* Add a PICKUPMARK variable to ringing interface */ 03474 if (option_debug > 2) 03475 ast_log(LOG_DEBUG, "chan %s, tech: %s, part %s\n", tmp->chan->name, tech, location); 03476 /* Delete DAHDI ring pattern in tech like DAHDI/1r2 */ 03477 ast_copy_string(chan, location, sizeof(chan)); 03478 if (!strncasecmp(tech, "dahdi", 5)) { 03479 if((chan[0] > '0') && (chan[0] <= '9')) { 03480 if ((location2 = strchr(chan, 'r'))) 03481 *location2++ = '\0'; 03482 } 03483 } 03484 snprintf(pickupmark, sizeof(pickupmark), "%s/%s", tech, chan); 03485 pbx_builtin_setvar_helper(tmp->chan, "PICKUPMARK", pickupmark); 03486 03487 /* Place the call, but don't wait on the answer */ 03488 if ((res = ast_call(tmp->chan, location, 0))) { 03489 /* Again, keep going even if there's an error */ 03490 ast_verb(3, "Couldn't call %s\n", tmp->interface); 03491 do_hang(tmp); 03492 member_call_pending_clear(tmp->member); 03493 ++*busies; 03494 return 0; 03495 } 03496 03497 if (qe->parent->eventwhencalled) { 03498 char vars[2048]; 03499 03500 ast_channel_lock_both(tmp->chan, qe->chan); 03501 03502 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 03503 "Queue: %s\r\n" 03504 "AgentCalled: %s\r\n" 03505 "AgentName: %s\r\n" 03506 "ChannelCalling: %s\r\n" 03507 "DestinationChannel: %s\r\n" 03508 "CallerIDNum: %s\r\n" 03509 "CallerIDName: %s\r\n" 03510 "ConnectedLineNum: %s\r\n" 03511 "ConnectedLineName: %s\r\n" 03512 "Context: %s\r\n" 03513 "Extension: %s\r\n" 03514 "Priority: %d\r\n" 03515 "Uniqueid: %s\r\n" 03516 "%s", 03517 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name, 03518 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"), 03519 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 03520 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"), 03521 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"), 03522 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid, 03523 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03524 03525 ast_channel_unlock(tmp->chan); 03526 ast_channel_unlock(qe->chan); 03527 03528 ast_verb(3, "Called %s\n", tmp->interface); 03529 } 03530 03531 member_call_pending_clear(tmp->member); 03532 return 1; 03533 }
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
1 | if a member was called successfully | |
0 | otherwise |
Definition at line 3561 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().
03562 { 03563 int ret = 0; 03564 03565 while (ret == 0) { 03566 struct callattempt *best = find_best(outgoing); 03567 if (!best) { 03568 ast_debug(1, "Nobody left to try ringing in queue\n"); 03569 break; 03570 } 03571 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 03572 struct callattempt *cur; 03573 /* Ring everyone who shares this best metric (for ringall) */ 03574 for (cur = outgoing; cur; cur = cur->q_next) { 03575 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 03576 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 03577 ret |= ring_entry(qe, cur, busies); 03578 } 03579 } 03580 } else { 03581 /* Ring just the best channel */ 03582 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric); 03583 ret = ring_entry(qe, best, busies); 03584 } 03585 03586 /* If we have timed out, break out */ 03587 if (qe->expire && (time(NULL) >= qe->expire)) { 03588 ast_debug(1, "Queue timed out while ringing members.\n"); 03589 ret = 0; 03590 break; 03591 } 03592 } 03593 03594 return ret; 03595 }
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 3719 of file app_queue.c.
References ast_indicate(), ast_moh_start(), ast_queue_log(), ast_verb, call_queue::autopause, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, manager_event, queue_ent::moh, queue_ent::parent, QUEUE_AUTOPAUSE_OFF, QUEUE_AUTOPAUSE_ON, QUEUE_EVENT_VARIABLES, queue_ent::ring_when_ringing, set_member_paused(), and vars2manager().
Referenced by wait_for_answer().
03720 { 03721 ast_verb(3, "Nobody picked up in %d ms\n", rnatime); 03722 03723 /* Stop ringing, and resume MOH if specified */ 03724 if (qe->ring_when_ringing) { 03725 ast_indicate(qe->chan, -1); 03726 ast_moh_start(qe->chan, qe->moh, NULL); 03727 } 03728 03729 if (qe->parent->eventwhencalled) { 03730 char vars[2048]; 03731 03732 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer", 03733 "Queue: %s\r\n" 03734 "Uniqueid: %s\r\n" 03735 "Channel: %s\r\n" 03736 "Member: %s\r\n" 03737 "MemberName: %s\r\n" 03738 "Ringtime: %d\r\n" 03739 "%s", 03740 qe->parent->name, 03741 qe->chan->uniqueid, 03742 qe->chan->name, 03743 interface, 03744 membername, 03745 rnatime, 03746 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03747 } 03748 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 03749 if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) { 03750 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) { 03751 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { 03752 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", 03753 interface, qe->parent->name); 03754 } else { 03755 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 03756 } 03757 } else { 03758 /* If queue autopause is mode all, just don't send any queue to stop. 03759 * the function will stop in all queues */ 03760 if (!set_member_paused("", interface, "Auto-Pause", 1)) { 03761 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n", 03762 interface, qe->parent->name); 03763 } else { 03764 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface); 03765 } 03766 } 03767 } 03768 return; 03769 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
RemoveQueueMember application.
Definition at line 6135 of file app_queue.c.
References args, AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.
Referenced by load_module().
06136 { 06137 int res=-1; 06138 char *parse, *temppos = NULL; 06139 AST_DECLARE_APP_ARGS(args, 06140 AST_APP_ARG(queuename); 06141 AST_APP_ARG(interface); 06142 ); 06143 06144 06145 if (ast_strlen_zero(data)) { 06146 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n"); 06147 return -1; 06148 } 06149 06150 parse = ast_strdupa(data); 06151 06152 AST_STANDARD_APP_ARGS(args, parse); 06153 06154 if (ast_strlen_zero(args.interface)) { 06155 args.interface = ast_strdupa(chan->name); 06156 temppos = strrchr(args.interface, '-'); 06157 if (temppos) 06158 *temppos = '\0'; 06159 } 06160 06161 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface); 06162 06163 switch (remove_from_queue(args.queuename, args.interface)) { 06164 case RES_OKAY: 06165 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 06166 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 06167 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 06168 res = 0; 06169 break; 06170 case RES_EXISTS: 06171 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 06172 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 06173 res = 0; 06174 break; 06175 case RES_NOSUCHQUEUE: 06176 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 06177 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 06178 res = 0; 06179 break; 06180 case RES_NOT_DYNAMIC: 06181 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 06182 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 06183 res = 0; 06184 break; 06185 } 06186 06187 return res; 06188 }
static void rt_handle_member_record | ( | struct call_queue * | q, | |
char * | interface, | |||
const char * | rt_uniqueid, | |||
const char * | membername, | |||
const char * | penalty_str, | |||
const char * | paused_str, | |||
const char * | state_interface | |||
) | [static] |
Find rt member record to update otherwise create one.
Search for member in queue, if found update penalty/paused state, if no member exists create one flag it as a RT member and add to queue member list.
Definition at line 2324 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_copy_string(), ast_log(), ast_queue_log(), ast_strlen_zero(), create_queue_member(), member::dead, member::interface, LOG_WARNING, member_add_to_queue(), call_queue::members, member::paused, member::penalty, member::realtime, member::rt_uniqueid, S_OR, and member::state_interface.
Referenced by find_queue_by_name_rt(), and update_realtime_members().
02325 { 02326 struct member *m; 02327 struct ao2_iterator mem_iter; 02328 int penalty = 0; 02329 int paused = 0; 02330 int found = 0; 02331 02332 if (ast_strlen_zero(rt_uniqueid)) { 02333 ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL")); 02334 return; 02335 } 02336 02337 if (penalty_str) { 02338 penalty = atoi(penalty_str); 02339 if (penalty < 0) 02340 penalty = 0; 02341 } 02342 02343 if (paused_str) { 02344 paused = atoi(paused_str); 02345 if (paused < 0) 02346 paused = 0; 02347 } 02348 02349 /* Find member by realtime uniqueid and update */ 02350 mem_iter = ao2_iterator_init(q->members, 0); 02351 while ((m = ao2_iterator_next(&mem_iter))) { 02352 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) { 02353 m->dead = 0; /* Do not delete this one. */ 02354 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 02355 if (paused_str) 02356 m->paused = paused; 02357 if (strcasecmp(state_interface, m->state_interface)) { 02358 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); 02359 } 02360 m->penalty = penalty; 02361 found = 1; 02362 ao2_ref(m, -1); 02363 break; 02364 } 02365 ao2_ref(m, -1); 02366 } 02367 ao2_iterator_destroy(&mem_iter); 02368 02369 /* Create a new member */ 02370 if (!found) { 02371 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { 02372 m->dead = 0; 02373 m->realtime = 1; 02374 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 02375 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : ""); 02376 member_add_to_queue(q, m); 02377 ao2_ref(m, -1); 02378 m = NULL; 02379 } 02380 } 02381 }
static int say_periodic_announcement | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Playback announcement to queued members if period has elapsed.
Definition at line 3646 of file app_queue.c.
References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_random(), ast_str_buffer(), ast_str_strlen(), ast_verb, queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::moh, call_queue::numperiodicannounce, queue_ent::parent, call_queue::periodicannouncefrequency, play_file(), call_queue::randomperiodicannounce, call_queue::relativeperiodicannounce, call_queue::sound_periodicannounce, and valid_exit().
Referenced by queue_exec(), wait_for_answer(), and wait_our_turn().
03647 { 03648 int res = 0; 03649 time_t now; 03650 03651 /* Get the current time */ 03652 time(&now); 03653 03654 /* Check to see if it is time to announce */ 03655 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 03656 return 0; 03657 03658 /* Stop the music on hold so we can play our own file */ 03659 if (ringing) 03660 ast_indicate(qe->chan,-1); 03661 else 03662 ast_moh_stop(qe->chan); 03663 03664 ast_verb(3, "Playing periodic announcement\n"); 03665 03666 if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) { 03667 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce; 03668 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 03669 ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) { 03670 qe->last_periodic_announce_sound = 0; 03671 } 03672 03673 /* play the announcement */ 03674 res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])); 03675 03676 if (res > 0 && !valid_exit(qe, res)) 03677 res = 0; 03678 03679 /* Resume Music on Hold if the caller is going to stay in the queue */ 03680 if (!res) { 03681 if (ringing) 03682 ast_indicate(qe->chan, AST_CONTROL_RINGING); 03683 else 03684 ast_moh_start(qe->chan, qe->moh, NULL); 03685 } 03686 03687 /* update last_periodic_announce_time */ 03688 if (qe->parent->relativeperiodicannounce) 03689 time(&qe->last_periodic_announce_time); 03690 else 03691 qe->last_periodic_announce_time = now; 03692 03693 /* Update the current periodic announcement to the next announcement */ 03694 if (!qe->parent->randomperiodicannounce) { 03695 qe->last_periodic_announce_sound++; 03696 } 03697 03698 return res; 03699 }
static int say_position | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Definition at line 2845 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, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, queue_ent::start, and valid_exit().
Referenced by queue_exec(), wait_for_answer(), and wait_our_turn().
02846 { 02847 int res = 0, avgholdmins, avgholdsecs, announceposition = 0; 02848 int say_thanks = 1; 02849 time_t now; 02850 02851 /* Let minannouncefrequency seconds pass between the start of each position announcement */ 02852 time(&now); 02853 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) 02854 return 0; 02855 02856 /* If either our position has changed, or we are over the freq timer, say position */ 02857 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 02858 return 0; 02859 02860 if (ringing) { 02861 ast_indicate(qe->chan,-1); 02862 } else { 02863 ast_moh_stop(qe->chan); 02864 } 02865 02866 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES || 02867 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN || 02868 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT && 02869 qe->pos <= qe->parent->announcepositionlimit)) 02870 announceposition = 1; 02871 02872 02873 if (announceposition == 1) { 02874 /* Say we're next, if we are */ 02875 if (qe->pos == 1) { 02876 res = play_file(qe->chan, qe->parent->sound_next); 02877 if (res) 02878 goto playout; 02879 else 02880 goto posout; 02881 } else { 02882 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02883 /* More than Case*/ 02884 res = play_file(qe->chan, qe->parent->queue_quantity1); 02885 if (res) 02886 goto playout; 02887 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02888 if (res) 02889 goto playout; 02890 } else { 02891 /* Normal Case */ 02892 res = play_file(qe->chan, qe->parent->sound_thereare); 02893 if (res) 02894 goto playout; 02895 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02896 if (res) 02897 goto playout; 02898 } 02899 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02900 /* More than Case*/ 02901 res = play_file(qe->chan, qe->parent->queue_quantity2); 02902 if (res) 02903 goto playout; 02904 } else { 02905 res = play_file(qe->chan, qe->parent->sound_calls); 02906 if (res) 02907 goto playout; 02908 } 02909 } 02910 } 02911 /* Round hold time to nearest minute */ 02912 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 02913 02914 /* If they have specified a rounding then round the seconds as well */ 02915 if (qe->parent->roundingseconds) { 02916 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 02917 avgholdsecs *= qe->parent->roundingseconds; 02918 } else { 02919 avgholdsecs = 0; 02920 } 02921 02922 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 02923 02924 /* If the hold time is >1 min, if it's enabled, and if it's not 02925 supposed to be only once and we have already said it, say it */ 02926 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 02927 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 02928 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 02929 res = play_file(qe->chan, qe->parent->sound_holdtime); 02930 if (res) 02931 goto playout; 02932 02933 if (avgholdmins >= 1) { 02934 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 02935 if (res) 02936 goto playout; 02937 02938 if (avgholdmins == 1) { 02939 res = play_file(qe->chan, qe->parent->sound_minute); 02940 if (res) 02941 goto playout; 02942 } else { 02943 res = play_file(qe->chan, qe->parent->sound_minutes); 02944 if (res) 02945 goto playout; 02946 } 02947 } 02948 if (avgholdsecs >= 1) { 02949 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 02950 if (res) 02951 goto playout; 02952 02953 res = play_file(qe->chan, qe->parent->sound_seconds); 02954 if (res) 02955 goto playout; 02956 } 02957 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) { 02958 say_thanks = 0; 02959 } 02960 02961 posout: 02962 if (qe->parent->announceposition) { 02963 ast_verb(3, "Told %s in %s their queue position (which was %d)\n", 02964 qe->chan->name, qe->parent->name, qe->pos); 02965 } 02966 if (say_thanks) { 02967 res = play_file(qe->chan, qe->parent->sound_thanks); 02968 } 02969 playout: 02970 02971 if ((res > 0 && !valid_exit(qe, res))) 02972 res = 0; 02973 02974 /* Set our last_pos indicators */ 02975 qe->last_pos = now; 02976 qe->last_pos_said = qe->pos; 02977 02978 /* Don't restart music on hold if we're about to exit the caller from the queue */ 02979 if (!res) { 02980 if (ringing) { 02981 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02982 } else { 02983 ast_moh_start(qe->chan, qe->moh, NULL); 02984 } 02985 } 02986 return res; 02987 }
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 4633 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().
04636 { 04637 const char *reason = NULL; /* silence dumb compilers */ 04638 04639 if (!qe->parent->eventwhencalled) 04640 return; 04641 04642 switch (rsn) { 04643 case CALLER: 04644 reason = "caller"; 04645 break; 04646 case AGENT: 04647 reason = "agent"; 04648 break; 04649 case TRANSFER: 04650 reason = "transfer"; 04651 break; 04652 } 04653 04654 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 04655 "Queue: %s\r\n" 04656 "Uniqueid: %s\r\n" 04657 "Channel: %s\r\n" 04658 "Member: %s\r\n" 04659 "MemberName: %s\r\n" 04660 "HoldTime: %ld\r\n" 04661 "TalkTime: %ld\r\n" 04662 "Reason: %s\r\n" 04663 "%s", 04664 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 04665 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason, 04666 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : ""); 04667 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
const char * | reason, | |||
int | paused | |||
) | [static] |
Definition at line 5810 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, 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().
05811 { 05812 int found = 0; 05813 struct call_queue *q; 05814 struct member *mem; 05815 struct ao2_iterator queue_iter; 05816 int failed; 05817 05818 /* Special event for when all queues are paused - individual events still generated */ 05819 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 05820 if (ast_strlen_zero(queuename)) 05821 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 05822 05823 queue_iter = ao2_iterator_init(queues, 0); 05824 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) { 05825 ao2_lock(q); 05826 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 05827 if ((mem = interface_exists(q, interface))) { 05828 if (mem->paused == paused) { 05829 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 05830 } 05831 05832 failed = 0; 05833 if (mem->realtime) { 05834 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 05835 } 05836 05837 if (failed) { 05838 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface); 05839 ao2_ref(mem, -1); 05840 ao2_unlock(q); 05841 queue_t_unref(q, "Done with iterator"); 05842 continue; 05843 } 05844 found++; 05845 mem->paused = paused; 05846 05847 if (queue_persistent_members) 05848 dump_queue_members(q); 05849 05850 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, "")); 05851 05852 if (!ast_strlen_zero(reason)) { 05853 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 05854 "Queue: %s\r\n" 05855 "Location: %s\r\n" 05856 "MemberName: %s\r\n" 05857 "Paused: %d\r\n" 05858 "Reason: %s\r\n", 05859 q->name, mem->interface, mem->membername, paused, reason); 05860 } else { 05861 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 05862 "Queue: %s\r\n" 05863 "Location: %s\r\n" 05864 "MemberName: %s\r\n" 05865 "Paused: %d\r\n", 05866 q->name, mem->interface, mem->membername, paused); 05867 } 05868 ao2_ref(mem, -1); 05869 } 05870 } 05871 05872 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) { 05873 ao2_unlock(q); 05874 queue_t_unref(q, "Done with iterator"); 05875 break; 05876 } 05877 05878 ao2_unlock(q); 05879 queue_t_unref(q, "Done with iterator"); 05880 } 05881 ao2_iterator_destroy(&queue_iter); 05882 05883 return found ? RESULT_SUCCESS : RESULT_FAILURE; 05884 }
static int set_member_penalty | ( | const char * | queuename, | |
const char * | interface, | |||
int | penalty | |||
) | [static] |
Definition at line 5887 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, 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().
05888 { 05889 int foundinterface = 0, foundqueue = 0; 05890 struct call_queue *q; 05891 struct member *mem; 05892 struct ao2_iterator queue_iter; 05893 05894 if (penalty < 0) { 05895 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); 05896 return RESULT_FAILURE; 05897 } 05898 05899 queue_iter = ao2_iterator_init(queues, 0); 05900 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 05901 ao2_lock(q); 05902 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 05903 foundqueue++; 05904 if ((mem = interface_exists(q, interface))) { 05905 foundinterface++; 05906 mem->penalty = penalty; 05907 05908 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); 05909 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", 05910 "Queue: %s\r\n" 05911 "Location: %s\r\n" 05912 "Penalty: %d\r\n", 05913 q->name, mem->interface, penalty); 05914 ao2_ref(mem, -1); 05915 } 05916 } 05917 ao2_unlock(q); 05918 queue_t_unref(q, "Done with iterator"); 05919 } 05920 ao2_iterator_destroy(&queue_iter); 05921 05922 if (foundinterface) { 05923 return RESULT_SUCCESS; 05924 } else if (!foundqueue) { 05925 ast_log (LOG_ERROR, "Invalid queuename\n"); 05926 } else { 05927 ast_log (LOG_ERROR, "Invalid interface\n"); 05928 } 05929 05930 return RESULT_FAILURE; 05931 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 1337 of file app_queue.c.
References ARRAY_LEN, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
01338 { 01339 int i; 01340 01341 for (i = 0; i < ARRAY_LEN(queue_results); i++) { 01342 if (queue_results[i].id == res) { 01343 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 01344 return; 01345 } 01346 } 01347 }
static void set_queue_variables | ( | struct call_queue * | q, | |
struct ast_channel * | chan | |||
) | [static] |
Set variables of queue.
Definition at line 1486 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, call_queue::holdtime, int2strat(), call_queue::maxlen, pbx_builtin_setvar_multiple(), call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.
Referenced by end_bridge_callback(), queue_exec(), record_abandoned(), and try_calling().
01487 { 01488 char interfacevar[256]=""; 01489 float sl = 0; 01490 01491 ao2_lock(q); 01492 01493 if (q->setqueuevar) { 01494 sl = 0; 01495 if (q->callscompleted > 0) 01496 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 01497 01498 snprintf(interfacevar, sizeof(interfacevar), 01499 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 01500 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 01501 01502 ao2_unlock(q); 01503 01504 pbx_builtin_setvar_multiple(chan, interfacevar); 01505 } else { 01506 ao2_unlock(q); 01507 } 01508 }
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 4738 of file app_queue.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc, ast_free, ast_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, and queue_transfer_ds::starttime.
Referenced by try_calling().
04739 { 04740 struct ast_datastore *ds; 04741 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 04742 04743 if (!qtds) { 04744 ast_log(LOG_WARNING, "Memory allocation error!\n"); 04745 return NULL; 04746 } 04747 04748 ast_channel_lock(qe->chan); 04749 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) { 04750 ast_channel_unlock(qe->chan); 04751 ast_free(qtds); 04752 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 04753 return NULL; 04754 } 04755 04756 qtds->qe = qe; 04757 /* This member is refcounted in try_calling, so no need to add it here, too */ 04758 qtds->member = member; 04759 qtds->starttime = starttime; 04760 qtds->callcompletedinsl = callcompletedinsl; 04761 ds->data = qtds; 04762 ast_channel_datastore_add(qe->chan, ds); 04763 ast_channel_unlock(qe->chan); 04764 return ds; 04765 }
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 3622 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().
03623 { 03624 struct callattempt *best = find_best(outgoing); 03625 03626 if (best) { 03627 /* Ring just the best channel */ 03628 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 03629 qe->linpos = best->metric % 1000; 03630 } else { 03631 /* Just increment rrpos */ 03632 if (qe->linwrapped) { 03633 /* No more channels, start over */ 03634 qe->linpos = 0; 03635 } else { 03636 /* Prioritize next entry */ 03637 qe->linpos++; 03638 } 03639 } 03640 qe->linwrapped = 0; 03641 03642 return 0; 03643 }
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 3598 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().
03599 { 03600 struct callattempt *best = find_best(outgoing); 03601 03602 if (best) { 03603 /* Ring just the best channel */ 03604 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 03605 qe->parent->rrpos = best->metric % 1000; 03606 } else { 03607 /* Just increment rrpos */ 03608 if (qe->parent->wrapped) { 03609 /* No more channels, start over */ 03610 qe->parent->rrpos = 0; 03611 } else { 03612 /* Prioritize next entry */ 03613 qe->parent->rrpos++; 03614 } 03615 } 03616 qe->parent->wrapped = 0; 03617 03618 return 0; 03619 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 1361 of file app_queue.c.
References ARRAY_LEN, and strategies.
Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_single_queue().
01362 { 01363 int x; 01364 01365 for (x = 0; x < ARRAY_LEN(strategies); x++) { 01366 if (!strcasecmp(strategy, strategies[x].name)) 01367 return strategies[x].strategy; 01368 } 01369 01370 return -1; 01371 }
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.
[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 4819 of file app_queue.c.
References ast_channel::_softhangup, ast_channel::_state, AGENT, queue_ent::announce, ao2_alloc, ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_asprintf, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_append(), ast_cdr_dup(), ast_cdr_failed(), AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_init(), ast_cdr_isset_unanswered(), ast_cdr_reset(), 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_exists_extension(), AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, AST_FLAG_ANSWERED_ELSEWHERE, ast_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_party_connected_line_copy(), 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(), callattempt::block_connected_update, calc_metric(), callattempt_free(), CALLER, ast_channel::caller, member::calls, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, queue_end_bridge::chan, callattempt::chan, queue_ent::chan, ast_channel::connected, callattempt::connected, 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, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, queue_ent::handled, hangupcalls(), ast_party_caller::id, ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), ast_cdr::linkedid, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event, callattempt::member, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, ast_cdr::next, ast_pbx_args::no_hangup_chan, ast_party_id::number, queue_ent::opos, option_debug, 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_STRATEGY_RRORDERED, queue_t_ref, member::realtime, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, S_COR, send_agent_complete(), call_queue::servicelevel, set_queue_variables(), call_queue::setinterfacevar, call_queue::setqueueentryvar, setup_transfer_datastore(), queue_ent::start, callattempt::stillgoing, store_next_lin(), store_next_rr(), ast_party_number::str, call_queue::strategy, ast_channel::tech, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, TRANSFER, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), ast_party_number::valid, vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.
Referenced by queue_exec().
04820 { 04821 struct member *cur; 04822 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 04823 int to, orig; 04824 char oldexten[AST_MAX_EXTENSION]=""; 04825 char oldcontext[AST_MAX_CONTEXT]=""; 04826 char queuename[256]=""; 04827 char interfacevar[256]=""; 04828 struct ast_channel *peer; 04829 struct ast_channel *which; 04830 struct callattempt *lpeer; 04831 struct member *member; 04832 struct ast_app *application; 04833 int res = 0, bridge = 0; 04834 int numbusies = 0; 04835 int x=0; 04836 char *announce = NULL; 04837 char digit = 0; 04838 time_t callstart; 04839 time_t now = time(NULL); 04840 struct ast_bridge_config bridge_config; 04841 char nondataquality = 1; 04842 char *agiexec = NULL; 04843 char *macroexec = NULL; 04844 char *gosubexec = NULL; 04845 const char *monitorfilename; 04846 const char *monitor_exec; 04847 const char *monitor_options; 04848 char tmpid[256], tmpid2[256]; 04849 char meid[1024], meid2[1024]; 04850 char mixmonargs[1512]; 04851 struct ast_app *mixmonapp = NULL; 04852 char *p; 04853 char vars[2048]; 04854 int forwardsallowed = 1; 04855 int block_connected_line = 0; 04856 int callcompletedinsl; 04857 struct ao2_iterator memi; 04858 struct ast_datastore *datastore, *transfer_ds; 04859 struct queue_end_bridge *queue_end_bridge = NULL; 04860 04861 ast_channel_lock(qe->chan); 04862 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 04863 ast_channel_unlock(qe->chan); 04864 04865 memset(&bridge_config, 0, sizeof(bridge_config)); 04866 tmpid[0] = 0; 04867 meid[0] = 0; 04868 time(&now); 04869 04870 /* If we've already exceeded our timeout, then just stop 04871 * This should be extremely rare. queue_exec will take care 04872 * of removing the caller and reporting the timeout as the reason. 04873 */ 04874 if (qe->expire && now >= qe->expire) { 04875 res = 0; 04876 goto out; 04877 } 04878 04879 for (; options && *options; options++) 04880 switch (*options) { 04881 case 't': 04882 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 04883 break; 04884 case 'T': 04885 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 04886 break; 04887 case 'w': 04888 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 04889 break; 04890 case 'W': 04891 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 04892 break; 04893 case 'c': 04894 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN); 04895 break; 04896 case 'd': 04897 nondataquality = 0; 04898 break; 04899 case 'h': 04900 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 04901 break; 04902 case 'H': 04903 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 04904 break; 04905 case 'k': 04906 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL); 04907 break; 04908 case 'K': 04909 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL); 04910 break; 04911 case 'n': 04912 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) 04913 (*tries)++; 04914 else 04915 *tries = ao2_container_count(qe->parent->members); 04916 *noption = 1; 04917 break; 04918 case 'i': 04919 forwardsallowed = 0; 04920 break; 04921 case 'I': 04922 block_connected_line = 1; 04923 break; 04924 case 'x': 04925 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); 04926 break; 04927 case 'X': 04928 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON); 04929 break; 04930 case 'C': 04931 qe->cancel_answered_elsewhere = 1; 04932 break; 04933 } 04934 04935 /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 04936 (this is mainly to support chan_local) 04937 */ 04938 if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) { 04939 qe->cancel_answered_elsewhere = 1; 04940 } 04941 04942 ao2_lock(qe->parent); 04943 ast_debug(1, "%s is trying to call a queue member.\n", 04944 qe->chan->name); 04945 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 04946 if (!ast_strlen_zero(qe->announce)) 04947 announce = qe->announce; 04948 if (!ast_strlen_zero(announceoverride)) 04949 announce = announceoverride; 04950 04951 memi = ao2_iterator_init(qe->parent->members, 0); 04952 while ((cur = ao2_iterator_next(&memi))) { 04953 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 04954 struct ast_dialed_interface *di; 04955 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 04956 if (!tmp) { 04957 ao2_ref(cur, -1); 04958 ao2_iterator_destroy(&memi); 04959 ao2_unlock(qe->parent); 04960 goto out; 04961 } 04962 if (!datastore) { 04963 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { 04964 callattempt_free(tmp); 04965 ao2_ref(cur, -1); 04966 ao2_iterator_destroy(&memi); 04967 ao2_unlock(qe->parent); 04968 goto out; 04969 } 04970 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 04971 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 04972 callattempt_free(tmp); 04973 ao2_ref(cur, -1); 04974 ao2_iterator_destroy(&memi); 04975 ao2_unlock(qe->parent); 04976 goto out; 04977 } 04978 datastore->data = dialed_interfaces; 04979 AST_LIST_HEAD_INIT(dialed_interfaces); 04980 04981 ast_channel_lock(qe->chan); 04982 ast_channel_datastore_add(qe->chan, datastore); 04983 ast_channel_unlock(qe->chan); 04984 } else 04985 dialed_interfaces = datastore->data; 04986 04987 AST_LIST_LOCK(dialed_interfaces); 04988 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 04989 if (!strcasecmp(cur->interface, di->interface)) { 04990 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 04991 di->interface); 04992 break; 04993 } 04994 } 04995 AST_LIST_UNLOCK(dialed_interfaces); 04996 04997 if (di) { 04998 callattempt_free(tmp); 04999 ao2_ref(cur, -1); 05000 continue; 05001 } 05002 05003 /* It is always ok to dial a Local interface. We only keep track of 05004 * which "real" interfaces have been dialed. The Local channel will 05005 * inherit this list so that if it ends up dialing a real interface, 05006 * it won't call one that has already been called. */ 05007 if (strncasecmp(cur->interface, "Local/", 6)) { 05008 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 05009 callattempt_free(tmp); 05010 ao2_ref(cur, -1); 05011 ao2_iterator_destroy(&memi); 05012 ao2_unlock(qe->parent); 05013 goto out; 05014 } 05015 strcpy(di->interface, cur->interface); 05016 05017 AST_LIST_LOCK(dialed_interfaces); 05018 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 05019 AST_LIST_UNLOCK(dialed_interfaces); 05020 } 05021 05022 /* 05023 * Seed the callattempt's connected line information with previously 05024 * acquired connected line info from the queued channel. The 05025 * previously acquired connected line info could have been set 05026 * through the CONNECTED_LINE dialplan function. 05027 */ 05028 ast_channel_lock(qe->chan); 05029 ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); 05030 ast_channel_unlock(qe->chan); 05031 05032 tmp->block_connected_update = block_connected_line; 05033 tmp->stillgoing = 1; 05034 tmp->member = cur;/* Place the reference for cur into callattempt. */ 05035 tmp->lastcall = cur->lastcall; 05036 tmp->lastqueue = cur->lastqueue; 05037 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 05038 if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) { 05039 cur->ringcount = 0; 05040 } 05041 /* Special case: If we ring everyone, go ahead and ring them, otherwise 05042 just calculate their metric for the appropriate strategy */ 05043 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 05044 /* Put them in the list of outgoing thingies... We're ready now. 05045 XXX If we're forcibly removed, these outgoing calls won't get 05046 hung up XXX */ 05047 tmp->q_next = outgoing; 05048 outgoing = tmp; 05049 /* If this line is up, don't try anybody else */ 05050 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 05051 break; 05052 } else { 05053 callattempt_free(tmp); 05054 } 05055 } 05056 ao2_iterator_destroy(&memi); 05057 05058 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) { 05059 /* Application arguments have higher timeout priority (behaviour for <=1.6) */ 05060 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 05061 to = (qe->expire - now) * 1000; 05062 else 05063 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 05064 } else { 05065 /* Config timeout is higher priority thatn application timeout */ 05066 if (qe->expire && qe->expire<=now) { 05067 to = 0; 05068 } else if (qe->parent->timeout) { 05069 to = qe->parent->timeout * 1000; 05070 } else { 05071 to = -1; 05072 } 05073 } 05074 orig = to; 05075 ++qe->pending; 05076 ++qe->tries; 05077 if (option_debug) 05078 ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n", 05079 qe->chan->name, queuename, qe->tries); 05080 ao2_unlock(qe->parent); 05081 ring_one(qe, outgoing, &numbusies); 05082 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, 05083 ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), 05084 forwardsallowed, ringing); 05085 /* The ast_channel_datastore_remove() function could fail here if the 05086 * datastore was moved to another channel during a masquerade. If this is 05087 * the case, don't free the datastore here because later, when the channel 05088 * to which the datastore was moved hangs up, it will attempt to free this 05089 * datastore again, causing a crash 05090 */ 05091 ast_channel_lock(qe->chan); 05092 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 05093 ast_datastore_free(datastore); 05094 } 05095 ast_channel_unlock(qe->chan); 05096 ao2_lock(qe->parent); 05097 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) { 05098 store_next_rr(qe, outgoing); 05099 05100 } 05101 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) { 05102 store_next_lin(qe, outgoing); 05103 } 05104 ao2_unlock(qe->parent); 05105 peer = lpeer ? lpeer->chan : NULL; 05106 if (!peer) { 05107 qe->pending = 0; 05108 if (to) { 05109 /* Must gotten hung up */ 05110 res = -1; 05111 } else { 05112 /* User exited by pressing a digit */ 05113 res = digit; 05114 } 05115 if (option_debug && res == -1) 05116 ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name); 05117 if (ast_cdr_isset_unanswered()) { 05118 /* channel contains the name of one of the outgoing channels 05119 in its CDR; zero out this CDR to avoid a dual-posting */ 05120 struct callattempt *o; 05121 for (o = outgoing; o; o = o->q_next) { 05122 if (!o->chan) { 05123 continue; 05124 } 05125 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) { 05126 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED); 05127 break; 05128 } 05129 } 05130 } 05131 } else { /* peer is valid */ 05132 /* Ah ha! Someone answered within the desired timeframe. Of course after this 05133 we will always return with -1 so that it is hung up properly after the 05134 conversation. */ 05135 if (!strcmp(qe->chan->tech->type, "DAHDI")) 05136 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 05137 if (!strcmp(peer->tech->type, "DAHDI")) 05138 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 05139 /* Update parameters for the queue */ 05140 time(&now); 05141 recalc_holdtime(qe, (now - qe->start)); 05142 ao2_lock(qe->parent); 05143 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 05144 ao2_unlock(qe->parent); 05145 member = lpeer->member; 05146 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 05147 ao2_ref(member, 1); 05148 hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere); 05149 outgoing = NULL; 05150 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 05151 int res2; 05152 05153 res2 = ast_autoservice_start(qe->chan); 05154 if (!res2) { 05155 if (qe->parent->memberdelay) { 05156 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 05157 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 05158 } 05159 if (!res2 && announce) { 05160 if (play_file(peer, announce) < 0) { 05161 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name); 05162 } 05163 } 05164 if (!res2 && qe->parent->reportholdtime) { 05165 if (!play_file(peer, qe->parent->sound_reporthold)) { 05166 int holdtime, holdtimesecs; 05167 05168 time(&now); 05169 holdtime = abs((now - qe->start) / 60); 05170 holdtimesecs = abs((now - qe->start) % 60); 05171 if (holdtime > 0) { 05172 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 05173 if (play_file(peer, qe->parent->sound_minutes) < 0) { 05174 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name); 05175 } 05176 } 05177 if (holdtimesecs > 1) { 05178 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL); 05179 if (play_file(peer, qe->parent->sound_seconds) < 0) { 05180 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name); 05181 } 05182 } 05183 } 05184 } 05185 ast_autoservice_stop(qe->chan); 05186 } 05187 if (ast_check_hangup(peer)) { 05188 /* Agent must have hung up */ 05189 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 05190 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 05191 if (qe->parent->eventwhencalled) 05192 manager_event(EVENT_FLAG_AGENT, "AgentDump", 05193 "Queue: %s\r\n" 05194 "Uniqueid: %s\r\n" 05195 "Channel: %s\r\n" 05196 "Member: %s\r\n" 05197 "MemberName: %s\r\n" 05198 "%s", 05199 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 05200 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 05201 ast_hangup(peer); 05202 ao2_ref(member, -1); 05203 goto out; 05204 } else if (ast_check_hangup(qe->chan)) { 05205 /* Caller must have hung up just before being connected */ 05206 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 05207 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 05208 record_abandoned(qe); 05209 ast_hangup(peer); 05210 ao2_ref(member, -1); 05211 return -1; 05212 } 05213 } 05214 /* Stop music on hold */ 05215 if (ringing) 05216 ast_indicate(qe->chan,-1); 05217 else 05218 ast_moh_stop(qe->chan); 05219 /* If appropriate, log that we have a destination channel */ 05220 if (qe->chan->cdr) 05221 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 05222 /* Make sure channels are compatible */ 05223 res = ast_channel_make_compatible(qe->chan, peer); 05224 if (res < 0) { 05225 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 05226 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 05227 record_abandoned(qe); 05228 ast_cdr_failed(qe->chan->cdr); 05229 ast_hangup(peer); 05230 ao2_ref(member, -1); 05231 return -1; 05232 } 05233 05234 /* Play announcement to the caller telling it's his turn if defined */ 05235 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) { 05236 if (play_file(qe->chan, qe->parent->sound_callerannounce)) 05237 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce); 05238 } 05239 05240 ao2_lock(qe->parent); 05241 /* if setinterfacevar is defined, make member variables available to the channel */ 05242 /* use pbx_builtin_setvar to set a load of variables with one call */ 05243 if (qe->parent->setinterfacevar) { 05244 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d", 05245 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime); 05246 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 05247 pbx_builtin_setvar_multiple(peer, interfacevar); 05248 } 05249 05250 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */ 05251 /* use pbx_builtin_setvar to set a load of variables with one call */ 05252 if (qe->parent->setqueueentryvar) { 05253 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d", 05254 (long) time(NULL) - qe->start, qe->opos); 05255 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 05256 pbx_builtin_setvar_multiple(peer, interfacevar); 05257 } 05258 05259 ao2_unlock(qe->parent); 05260 05261 /* try to set queue variables if configured to do so*/ 05262 set_queue_variables(qe->parent, qe->chan); 05263 set_queue_variables(qe->parent, peer); 05264 05265 ast_channel_lock(qe->chan); 05266 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) { 05267 monitorfilename = ast_strdupa(monitorfilename); 05268 } 05269 ast_channel_unlock(qe->chan); 05270 /* Begin Monitoring */ 05271 if (qe->parent->monfmt && *qe->parent->monfmt) { 05272 if (!qe->parent->montype) { 05273 const char *monexec; 05274 ast_debug(1, "Starting Monitor as requested.\n"); 05275 ast_channel_lock(qe->chan); 05276 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) { 05277 which = qe->chan; 05278 monexec = monexec ? ast_strdupa(monexec) : NULL; 05279 } 05280 else 05281 which = peer; 05282 ast_channel_unlock(qe->chan); 05283 if (monitorfilename) { 05284 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); 05285 } else if (qe->chan->cdr) { 05286 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); 05287 } else { 05288 /* Last ditch effort -- no CDR, make up something */ 05289 snprintf(tmpid, sizeof(tmpid), "chan-%lx", (unsigned long)ast_random()); 05290 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); 05291 } 05292 if (!ast_strlen_zero(monexec)) { 05293 ast_monitor_setjoinfiles(which, 1); 05294 } 05295 } else { 05296 mixmonapp = pbx_findapp("MixMonitor"); 05297 05298 if (mixmonapp) { 05299 ast_debug(1, "Starting MixMonitor as requested.\n"); 05300 if (!monitorfilename) { 05301 if (qe->chan->cdr) 05302 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)); 05303 else 05304 snprintf(tmpid, sizeof(tmpid), "chan-%lx", (unsigned long)ast_random()); 05305 } else { 05306 const char *m = monitorfilename; 05307 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) { 05308 switch (*m) { 05309 case '^': 05310 if (*(m + 1) == '{') 05311 *p = '$'; 05312 break; 05313 case ',': 05314 *p++ = '\\'; 05315 /* Fall through */ 05316 default: 05317 *p = *m; 05318 } 05319 if (*m == '\0') 05320 break; 05321 } 05322 if (p == tmpid2 + sizeof(tmpid2)) 05323 tmpid2[sizeof(tmpid2) - 1] = '\0'; 05324 05325 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 05326 } 05327 05328 ast_channel_lock(qe->chan); 05329 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) { 05330 monitor_exec = ast_strdupa(monitor_exec); 05331 } 05332 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) { 05333 monitor_options = ast_strdupa(monitor_options); 05334 } else { 05335 monitor_options = ""; 05336 } 05337 ast_channel_unlock(qe->chan); 05338 05339 if (monitor_exec) { 05340 const char *m = monitor_exec; 05341 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) { 05342 switch (*m) { 05343 case '^': 05344 if (*(m + 1) == '{') 05345 *p = '$'; 05346 break; 05347 case ',': 05348 *p++ = '\\'; 05349 /* Fall through */ 05350 default: 05351 *p = *m; 05352 } 05353 if (*m == '\0') 05354 break; 05355 } 05356 if (p == meid2 + sizeof(meid2)) 05357 meid2[sizeof(meid2) - 1] = '\0'; 05358 05359 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 05360 } 05361 05362 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt); 05363 05364 if (!ast_strlen_zero(monitor_exec)) 05365 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec); 05366 else 05367 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options); 05368 05369 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 05370 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 05371 if (qe->chan->cdr) 05372 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 05373 pbx_exec(qe->chan, mixmonapp, mixmonargs); 05374 if (qe->chan->cdr) 05375 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 05376 05377 } else { 05378 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 05379 } 05380 } 05381 } 05382 /* Drop out of the queue at this point, to prepare for next caller */ 05383 leave_queue(qe); 05384 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 05385 ast_debug(1, "app_queue: sendurl=%s.\n", url); 05386 ast_channel_sendurl(peer, url); 05387 } 05388 05389 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */ 05390 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */ 05391 if (!ast_strlen_zero(macro)) { 05392 macroexec = ast_strdupa(macro); 05393 } else { 05394 if (qe->parent->membermacro) 05395 macroexec = ast_strdupa(qe->parent->membermacro); 05396 } 05397 05398 if (!ast_strlen_zero(macroexec)) { 05399 ast_debug(1, "app_queue: macro=%s.\n", macroexec); 05400 05401 res = ast_autoservice_start(qe->chan); 05402 if (res) { 05403 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 05404 res = -1; 05405 } 05406 05407 application = pbx_findapp("Macro"); 05408 05409 if (application) { 05410 res = pbx_exec(peer, application, macroexec); 05411 ast_debug(1, "Macro exited with status %d\n", res); 05412 res = 0; 05413 } else { 05414 ast_log(LOG_ERROR, "Could not find application Macro\n"); 05415 res = -1; 05416 } 05417 05418 if (ast_autoservice_stop(qe->chan) < 0) { 05419 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 05420 res = -1; 05421 } 05422 } 05423 05424 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */ 05425 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */ 05426 if (!ast_strlen_zero(gosub)) { 05427 gosubexec = ast_strdupa(gosub); 05428 } else { 05429 if (qe->parent->membergosub) 05430 gosubexec = ast_strdupa(qe->parent->membergosub); 05431 } 05432 05433 if (!ast_strlen_zero(gosubexec)) { 05434 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec); 05435 05436 res = ast_autoservice_start(qe->chan); 05437 if (res) { 05438 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 05439 res = -1; 05440 } 05441 05442 application = pbx_findapp("Gosub"); 05443 05444 if (application) { 05445 char *gosub_args, *gosub_argstart; 05446 05447 /* Set where we came from */ 05448 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context)); 05449 ast_copy_string(peer->exten, "s", sizeof(peer->exten)); 05450 peer->priority = 0; 05451 05452 gosub_argstart = strchr(gosubexec, ','); 05453 if (gosub_argstart) { 05454 const char *what_is_s = "s"; 05455 *gosub_argstart = 0; 05456 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) && 05457 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) { 05458 what_is_s = "~~s~~"; 05459 } 05460 if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) { 05461 gosub_args = NULL; 05462 } 05463 *gosub_argstart = ','; 05464 } else { 05465 const char *what_is_s = "s"; 05466 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) && 05467 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) { 05468 what_is_s = "~~s~~"; 05469 } 05470 if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) { 05471 gosub_args = NULL; 05472 } 05473 } 05474 if (gosub_args) { 05475 res = pbx_exec(peer, application, gosub_args); 05476 if (!res) { 05477 struct ast_pbx_args args; 05478 memset(&args, 0, sizeof(args)); 05479 args.no_hangup_chan = 1; 05480 ast_pbx_run_args(peer, &args); 05481 } 05482 ast_free(gosub_args); 05483 ast_debug(1, "Gosub exited with status %d\n", res); 05484 } else { 05485 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); 05486 } 05487 } else { 05488 ast_log(LOG_ERROR, "Could not find application Gosub\n"); 05489 res = -1; 05490 } 05491 05492 if (ast_autoservice_stop(qe->chan) < 0) { 05493 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 05494 res = -1; 05495 } 05496 } 05497 05498 if (!ast_strlen_zero(agi)) { 05499 ast_debug(1, "app_queue: agi=%s.\n", agi); 05500 application = pbx_findapp("agi"); 05501 if (application) { 05502 agiexec = ast_strdupa(agi); 05503 pbx_exec(qe->chan, application, agiexec); 05504 } else 05505 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 05506 } 05507 qe->handled++; 05508 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid, 05509 (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); 05510 05511 if (qe->chan->cdr) { 05512 struct ast_cdr *cdr; 05513 struct ast_cdr *newcdr; 05514 05515 /* Only work with the last CDR in the stack*/ 05516 cdr = qe->chan->cdr; 05517 while (cdr->next) { 05518 cdr = cdr->next; 05519 } 05520 05521 /* If this CDR is not related to us add new one*/ 05522 if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) && 05523 (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) && 05524 (newcdr = ast_cdr_dup(cdr))) { 05525 ast_channel_lock(qe->chan); 05526 ast_cdr_init(newcdr, qe->chan); 05527 ast_cdr_reset(newcdr, 0); 05528 cdr = ast_cdr_append(cdr, newcdr); 05529 cdr = cdr->next; 05530 ast_channel_unlock(qe->chan); 05531 } 05532 05533 if (update_cdr) { 05534 ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel)); 05535 } 05536 } 05537 05538 if (qe->parent->eventwhencalled) 05539 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 05540 "Queue: %s\r\n" 05541 "Uniqueid: %s\r\n" 05542 "Channel: %s\r\n" 05543 "Member: %s\r\n" 05544 "MemberName: %s\r\n" 05545 "Holdtime: %ld\r\n" 05546 "BridgedChannel: %s\r\n" 05547 "Ringtime: %ld\r\n" 05548 "%s", 05549 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 05550 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0), 05551 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 05552 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 05553 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 05554 05555 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { 05556 queue_end_bridge->q = qe->parent; 05557 queue_end_bridge->chan = qe->chan; 05558 bridge_config.end_bridge_callback = end_bridge_callback; 05559 bridge_config.end_bridge_callback_data = queue_end_bridge; 05560 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; 05561 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need 05562 * to make sure to increase the refcount of this queue so it cannot be freed until we 05563 * are done with it. We remove this reference in end_bridge_callback. 05564 */ 05565 queue_t_ref(qe->parent, "For bridge_config reference"); 05566 } 05567 05568 time(&callstart); 05569 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 05570 bridge = ast_bridge_call(qe->chan, peer, &bridge_config); 05571 05572 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log 05573 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for 05574 * the AgentComplete manager event 05575 */ 05576 ast_channel_lock(qe->chan); 05577 if (!attended_transfer_occurred(qe->chan)) { 05578 struct ast_datastore *tds; 05579 05580 /* detect a blind transfer */ 05581 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) { 05582 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 05583 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 05584 (long) (time(NULL) - callstart), qe->opos); 05585 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 05586 } else if (ast_check_hangup(qe->chan) && !ast_check_hangup(peer)) { 05587 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 05588 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 05589 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER); 05590 } else { 05591 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 05592 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 05593 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT); 05594 } 05595 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 05596 ast_channel_datastore_remove(qe->chan, tds); 05597 /* tds was added by setup_transfer_datastore() and is freed below. */ 05598 } 05599 ast_channel_unlock(qe->chan); 05600 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 05601 } else { 05602 ast_channel_unlock(qe->chan); 05603 05604 /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */ 05605 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 05606 } 05607 05608 if (transfer_ds) { 05609 ast_datastore_free(transfer_ds); 05610 } 05611 ast_hangup(peer); 05612 res = bridge ? bridge : 1; 05613 ao2_ref(member, -1); 05614 } 05615 out: 05616 hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere); 05617 05618 return res; 05619 }
static int unload_module | ( | void | ) | [static] |
Definition at line 9061 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ao2_t_iterator_next, ARRAY_LEN, ast_cli_unregister_multiple(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_data_unregister, ast_event_unsubscribe(), ast_extension_state_del(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), extension_state_cb(), queue_t_unref, queues, and queues_t_unlink.
09062 { 09063 int res; 09064 struct ast_context *con; 09065 struct ao2_iterator q_iter; 09066 struct call_queue *q = NULL; 09067 09068 ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue)); 09069 res = ast_manager_unregister("QueueStatus"); 09070 res |= ast_manager_unregister("Queues"); 09071 res |= ast_manager_unregister("QueueRule"); 09072 res |= ast_manager_unregister("QueueSummary"); 09073 res |= ast_manager_unregister("QueueAdd"); 09074 res |= ast_manager_unregister("QueueRemove"); 09075 res |= ast_manager_unregister("QueuePause"); 09076 res |= ast_manager_unregister("QueueLog"); 09077 res |= ast_manager_unregister("QueuePenalty"); 09078 res |= ast_manager_unregister("QueueReload"); 09079 res |= ast_manager_unregister("QueueReset"); 09080 res |= ast_unregister_application(app_aqm); 09081 res |= ast_unregister_application(app_rqm); 09082 res |= ast_unregister_application(app_pqm); 09083 res |= ast_unregister_application(app_upqm); 09084 res |= ast_unregister_application(app_ql); 09085 res |= ast_unregister_application(app); 09086 res |= ast_custom_function_unregister(&queueexists_function); 09087 res |= ast_custom_function_unregister(&queuevar_function); 09088 res |= ast_custom_function_unregister(&queuemembercount_function); 09089 res |= ast_custom_function_unregister(&queuemembercount_dep); 09090 res |= ast_custom_function_unregister(&queuememberlist_function); 09091 res |= ast_custom_function_unregister(&queuewaitingcount_function); 09092 res |= ast_custom_function_unregister(&queuememberpenalty_function); 09093 res |= ast_custom_function_unregister(&queuememberstatus_function); 09094 res |= ast_custom_function_unregister(&queuememberpaused_function); 09095 09096 res |= ast_data_unregister(NULL); 09097 09098 if (device_state_sub) 09099 ast_event_unsubscribe(device_state_sub); 09100 09101 ast_extension_state_del(0, extension_state_cb); 09102 09103 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) { 09104 ast_context_remove_extension2(con, "s", 1, NULL, 0); 09105 ast_context_destroy(con, "app_queue"); /* leave no trace */ 09106 } 09107 09108 q_iter = ao2_iterator_init(queues, 0); 09109 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) { 09110 queues_t_unlink(queues, q, "Remove queue from container due to unload"); 09111 queue_t_unref(q, "Done with iterator"); 09112 } 09113 ao2_iterator_destroy(&q_iter); 09114 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps); 09115 ao2_ref(queues, -1); 09116 ast_unload_realtime("queue_members"); 09117 return res; 09118 }
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 4363 of file app_queue.c.
References ast_debug, AST_LIST_NEXT, queue_ent::chan, queue_ent::max_penalty, queue_ent::min_penalty, and pbx_builtin_setvar_helper().
Referenced by queue_exec(), and wait_our_turn().
04364 { 04365 int max_penalty = INT_MAX; 04366 04367 if (qe->max_penalty != INT_MAX) { 04368 char max_penalty_str[20]; 04369 04370 if (qe->pr->max_relative) { 04371 max_penalty = qe->max_penalty + qe->pr->max_value; 04372 } else { 04373 max_penalty = qe->pr->max_value; 04374 } 04375 04376 /* a relative change to the penalty could put it below 0 */ 04377 if (max_penalty < 0) { 04378 max_penalty = 0; 04379 } 04380 04381 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty); 04382 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str); 04383 qe->max_penalty = max_penalty; 04384 ast_debug(3, "Setting max penalty to %d for caller %s since %d seconds have elapsed\n", 04385 qe->max_penalty, qe->chan->name, qe->pr->time); 04386 } 04387 04388 if (qe->min_penalty != INT_MAX) { 04389 char min_penalty_str[20]; 04390 int min_penalty; 04391 04392 if (qe->pr->min_relative) { 04393 min_penalty = qe->min_penalty + qe->pr->min_value; 04394 } else { 04395 min_penalty = qe->pr->min_value; 04396 } 04397 04398 if (min_penalty < 0) { 04399 min_penalty = 0; 04400 } 04401 04402 if (max_penalty != INT_MAX && min_penalty > max_penalty) { 04403 min_penalty = max_penalty; 04404 } 04405 04406 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty); 04407 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str); 04408 qe->min_penalty = min_penalty; 04409 ast_debug(3, "Setting min penalty to %d for caller %s since %d seconds have elapsed\n", 04410 qe->min_penalty, qe->chan->name, qe->pr->time); 04411 } 04412 04413 qe->pr = AST_LIST_NEXT(qe->pr, list); 04414 }
static int update_queue | ( | struct call_queue * | q, | |
struct member * | member, | |||
int | callcompletedinsl, | |||
int | newtalktime | |||
) | [static] |
update the queue status
Always | 0 |
Definition at line 4502 of file app_queue.c.
References ao2_find, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, call_queue::members, OBJ_POINTER, queue_t_unref, queues, and call_queue::talktime.
Referenced by queue_transfer_fixup(), and try_calling().
04503 { 04504 int oldtalktime; 04505 04506 struct member *mem; 04507 struct call_queue *qtmp; 04508 struct ao2_iterator queue_iter; 04509 04510 if (shared_lastcall) { 04511 queue_iter = ao2_iterator_init(queues, 0); 04512 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 04513 ao2_lock(qtmp); 04514 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { 04515 time(&mem->lastcall); 04516 mem->calls++; 04517 mem->lastqueue = q; 04518 ao2_ref(mem, -1); 04519 } 04520 ao2_unlock(qtmp); 04521 queue_t_unref(qtmp, "Done with iterator"); 04522 } 04523 ao2_iterator_destroy(&queue_iter); 04524 } else { 04525 ao2_lock(q); 04526 time(&member->lastcall); 04527 member->calls++; 04528 member->lastqueue = q; 04529 ao2_unlock(q); 04530 } 04531 ao2_lock(q); 04532 q->callscompleted++; 04533 if (callcompletedinsl) 04534 q->callscompletedinsl++; 04535 /* Calculate talktime using the same exponential average as holdtime code*/ 04536 oldtalktime = q->talktime; 04537 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2; 04538 ao2_unlock(q); 04539 return 0; 04540 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 2624 of file app_queue.c.
References ast_strlen_zero(), ast_update_realtime(), member::rt_uniqueid, and SENTINEL.
Referenced by set_member_paused().
02625 { 02626 int ret = -1; 02627 02628 if (ast_strlen_zero(mem->rt_uniqueid)) 02629 return ret; 02630 02631 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) 02632 ret = 0; 02633 02634 return ret; 02635 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 2638 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, member_remove_from_queue(), call_queue::members, member::realtime, rt_handle_member_record(), S_OR, and SENTINEL.
Referenced by load_realtime_queue(), and queue_exec().
02639 { 02640 struct ast_config *member_config = NULL; 02641 struct member *m; 02642 char *interface = NULL; 02643 struct ao2_iterator mem_iter; 02644 02645 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) { 02646 /* This queue doesn't have realtime members. If the queue still has any realtime 02647 * members in memory, they need to be removed. 02648 */ 02649 ao2_lock(q); 02650 mem_iter = ao2_iterator_init(q->members, 0); 02651 while ((m = ao2_iterator_next(&mem_iter))) { 02652 if (m->realtime) { 02653 member_remove_from_queue(q, m); 02654 } 02655 ao2_ref(m, -1); 02656 } 02657 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name); 02658 ao2_unlock(q); 02659 return; 02660 } 02661 02662 ao2_lock(q); 02663 02664 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 02665 mem_iter = ao2_iterator_init(q->members, 0); 02666 while ((m = ao2_iterator_next(&mem_iter))) { 02667 if (m->realtime) 02668 m->dead = 1; 02669 ao2_ref(m, -1); 02670 } 02671 ao2_iterator_destroy(&mem_iter); 02672 02673 while ((interface = ast_category_browse(member_config, interface))) { 02674 rt_handle_member_record(q, interface, 02675 ast_variable_retrieve(member_config, interface, "uniqueid"), 02676 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 02677 ast_variable_retrieve(member_config, interface, "penalty"), 02678 ast_variable_retrieve(member_config, interface, "paused"), 02679 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface)); 02680 } 02681 02682 /* Delete all realtime members that have been deleted in DB. */ 02683 mem_iter = ao2_iterator_init(q->members, 0); 02684 while ((m = ao2_iterator_next(&mem_iter))) { 02685 if (m->dead) { 02686 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 02687 member_remove_from_queue(q, m); 02688 } 02689 ao2_ref(m, -1); 02690 } 02691 ao2_iterator_destroy(&mem_iter); 02692 ao2_unlock(q); 02693 ast_config_destroy(member_config); 02694 }
static int update_status | ( | struct call_queue * | q, | |
struct member * | m, | |||
const int | status | |||
) | [static] |
set a member's status based on device state of that member's state_interface.
Lock interface list find sc, iterate through each queues queue_member list for member to update state inside queues
Definition at line 1626 of file app_queue.c.
References member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event, call_queue::maskmemberstatus, member::membername, member::paused, member::penalty, member::realtime, and member::status.
Referenced by extension_state_cb(), and handle_statechange().
01627 { 01628 m->status = status; 01629 01630 if (q->maskmemberstatus) 01631 return 0; 01632 01633 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 01634 "Queue: %s\r\n" 01635 "Location: %s\r\n" 01636 "MemberName: %s\r\n" 01637 "Membership: %s\r\n" 01638 "Penalty: %d\r\n" 01639 "CallsTaken: %d\r\n" 01640 "LastCall: %d\r\n" 01641 "Status: %d\r\n" 01642 "Paused: %d\r\n", 01643 q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static", 01644 m->penalty, m->calls, (int)m->lastcall, m->status, m->paused 01645 ); 01646 01647 return 0; 01648 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
UnPauseQueueMember application.
Definition at line 6099 of file app_queue.c.
References args, AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
06100 { 06101 char *parse; 06102 AST_DECLARE_APP_ARGS(args, 06103 AST_APP_ARG(queuename); 06104 AST_APP_ARG(interface); 06105 AST_APP_ARG(options); 06106 AST_APP_ARG(reason); 06107 ); 06108 06109 if (ast_strlen_zero(data)) { 06110 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n"); 06111 return -1; 06112 } 06113 06114 parse = ast_strdupa(data); 06115 06116 AST_STANDARD_APP_ARGS(args, parse); 06117 06118 if (ast_strlen_zero(args.interface)) { 06119 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 06120 return -1; 06121 } 06122 06123 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) { 06124 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 06125 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 06126 return 0; 06127 } 06128 06129 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 06130 06131 return 0; 06132 }
static int valid_exit | ( | struct queue_ent * | qe, | |
char | digit | |||
) | [static] |
Check for valid exit from queue via goto.
0 | if failure | |
1 | if successful |
Definition at line 2811 of file app_queue.c.
References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), ast_channel::caller, queue_ent::chan, queue_ent::context, queue_ent::digits, ast_party_caller::id, ast_party_id::number, S_COR, ast_party_number::str, ast_party_number::valid, and queue_ent::valid_digits.
Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), wait_for_answer(), and wait_our_turn().
02812 { 02813 int digitlen = strlen(qe->digits); 02814 02815 /* Prevent possible buffer overflow */ 02816 if (digitlen < sizeof(qe->digits) - 2) { 02817 qe->digits[digitlen] = digit; 02818 qe->digits[digitlen + 1] = '\0'; 02819 } else { 02820 qe->digits[0] = '\0'; 02821 return 0; 02822 } 02823 02824 /* If there's no context to goto, short-circuit */ 02825 if (ast_strlen_zero(qe->context)) 02826 return 0; 02827 02828 /* If the extension is bad, then reset the digits to blank */ 02829 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, 02830 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) { 02831 qe->digits[0] = '\0'; 02832 return 0; 02833 } 02834 02835 /* We have an exact match */ 02836 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 02837 qe->valid_digits = 1; 02838 /* Return 1 on a successful goto */ 02839 return 1; 02840 } 02841 02842 return 0; 02843 }
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 3202 of file app_queue.c.
References ast_copy_string(), ast_str_buffer(), ast_str_thread_get(), and pbx_builtin_serialize_variables().
Referenced by ring_entry(), rna(), send_agent_complete(), and try_calling().
03203 { 03204 struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1); 03205 const char *tmp; 03206 03207 if (pbx_builtin_serialize_variables(chan, &buf)) { 03208 int i, j; 03209 03210 /* convert "\n" to "\nVariable: " */ 03211 strcpy(vars, "Variable: "); 03212 tmp = ast_str_buffer(buf); 03213 03214 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 03215 vars[j] = tmp[i]; 03216 03217 if (tmp[i + 1] == '\0') 03218 break; 03219 if (tmp[i] == '\n') { 03220 vars[j++] = '\r'; 03221 vars[j++] = '\n'; 03222 03223 ast_copy_string(&(vars[j]), "Variable: ", len - j); 03224 j += 9; 03225 } 03226 } 03227 if (j > len - 3) 03228 j = len - 3; 03229 vars[j++] = '\r'; 03230 vars[j++] = '\n'; 03231 vars[j] = '\0'; 03232 } else { 03233 /* there are no channel variables; leave it blank */ 03234 *vars = '\0'; 03235 } 03236 return vars; 03237 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 5621 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
05622 { 05623 /* Don't need to hold the lock while we setup the outgoing calls */ 05624 int retrywait = qe->parent->retry * 1000; 05625 05626 int res = ast_waitfordigit(qe->chan, retrywait); 05627 if (res > 0 && !valid_exit(qe, res)) 05628 res = 0; 05629 05630 return res; 05631 }
static struct callattempt* wait_for_answer | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing, | |||
int * | to, | |||
char * | digit, | |||
int | prebusies, | |||
int | caller_disconnect, | |||
int | forwardsallowed, | |||
int | ringing | |||
) | [static, read] |
Wait for a member to answer the call.
[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 3785 of file app_queue.c.
References ast_channel::_state, accountcode, call_queue::announce_to_first_user, call_queue::announcefrequency, callattempt::aoc_s_rate_list, ast_aoc_decode(), ast_aoc_destroy_decoded(), ast_aoc_destroy_encoded(), ast_aoc_encode(), ast_aoc_get_msg_type(), AST_AOC_S, ast_call(), ast_cdr_failed(), ast_cdr_noanswer(), AST_CEL_FORWARD, ast_cel_report_event(), ast_channel_connected_line_macro(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_lock_both, AST_CHANNEL_NAME, ast_channel_redirecting_macro(), ast_channel_unlock, ast_channel_update_connected_line(), ast_channel_update_redirecting(), ast_check_hangup(), ast_connected_line_copy_from_caller(), ast_connected_line_parse_data(), AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER, AST_CONTROL_ANSWER, AST_CONTROL_AOC, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_CONNECTED_LINE, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_REDIRECTING, AST_CONTROL_RINGING, ast_copy_string(), ast_debug, AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_indicate(), ast_indicate_data(), ast_log(), AST_MAX_WATCHERS, ast_moh_stop(), ast_party_connected_line_copy(), ast_party_connected_line_free(), ast_party_connected_line_init(), ast_party_connected_line_set(), ast_party_connected_line_set_init(), ast_party_number_free(), ast_party_number_init(), ast_party_redirecting_copy(), ast_party_redirecting_free(), ast_party_redirecting_init(), ast_poll_channel_add(), ast_poll_channel_del(), ast_read(), ast_remaining_ms(), ast_request(), AST_STATE_UP, ast_strdup, ast_strdupa, ast_string_field_set, ast_strlen_zero(), ast_tvnow(), ast_verb, ast_waitfor_n(), callattempt::block_connected_update, callattempt::call_next, ast_channel::caller, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::connected, callattempt::connected, ast_channel::context, ast_frame::data, ast_frame::datalen, callattempt::dial_callerid_absent, ast_channel::dialed, do_hang(), ast_channel::exten, f, ast_frame::frametype, ast_party_redirecting::from, ast_channel::hangupcause, ast_party_caller::id, ast_frame_subclass::integer, callattempt::interface, member::interface, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, ast_channel::nativeformats, ast_party_id::number, queue_ent::parent, callattempt::pending_connected_update, call_queue::periodicannouncefrequency, queue_ent::pos, ast_frame::ptr, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ast_channel::redirecting, ring_one(), queue_ent::ring_when_ringing, rna(), S_OR, say_periodic_announcement(), say_position(), ast_party_connected_line::source, queue_ent::start, status, callattempt::stillgoing, ast_party_number::str, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, ast_party_dialed::transit_network_select, ast_frame::uint32, ast_party_number::valid, and valid_exit().
Referenced by try_calling().
03786 { 03787 const char *queue = qe->parent->name; 03788 struct callattempt *o, *start = NULL, *prev = NULL; 03789 int status; 03790 int numbusies = prebusies; 03791 int numnochan = 0; 03792 int stillgoing = 0; 03793 int orig = *to; 03794 struct ast_frame *f; 03795 struct callattempt *peer = NULL; 03796 struct ast_channel *winner; 03797 struct ast_channel *in = qe->chan; 03798 char on[80] = ""; 03799 char membername[80] = ""; 03800 long starttime = 0; 03801 long endtime = 0; 03802 #ifdef HAVE_EPOLL 03803 struct callattempt *epollo; 03804 #endif 03805 struct ast_party_connected_line connected_caller; 03806 char *inchan_name; 03807 struct timeval start_time_tv = ast_tvnow(); 03808 03809 ast_party_connected_line_init(&connected_caller); 03810 03811 ast_channel_lock(qe->chan); 03812 inchan_name = ast_strdupa(qe->chan->name); 03813 ast_channel_unlock(qe->chan); 03814 03815 starttime = (long) time(NULL); 03816 #ifdef HAVE_EPOLL 03817 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 03818 if (epollo->chan) 03819 ast_poll_channel_add(in, epollo->chan); 03820 } 03821 #endif 03822 03823 while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) { 03824 int numlines, retry, pos = 1; 03825 struct ast_channel *watchers[AST_MAX_WATCHERS]; 03826 watchers[0] = in; 03827 start = NULL; 03828 03829 for (retry = 0; retry < 2; retry++) { 03830 numlines = 0; 03831 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 03832 if (o->stillgoing) { /* Keep track of important channels */ 03833 stillgoing = 1; 03834 if (o->chan) { 03835 if (pos < AST_MAX_WATCHERS) { 03836 watchers[pos++] = o->chan; 03837 } 03838 if (!start) 03839 start = o; 03840 else 03841 prev->call_next = o; 03842 prev = o; 03843 } 03844 } 03845 numlines++; 03846 } 03847 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 03848 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 03849 break; 03850 /* On "ringall" strategy we only move to the next penalty level 03851 when *all* ringing phones are done in the current penalty level */ 03852 ring_one(qe, outgoing, &numbusies); 03853 /* and retry... */ 03854 } 03855 if (pos == 1 /* not found */) { 03856 if (numlines == (numbusies + numnochan)) { 03857 ast_debug(1, "Everyone is busy at this time\n"); 03858 //if (in->cdr && in->_state != AST_STATE_UP) { 03859 // ast_cdr_busy(in->cdr); 03860 //} 03861 } else { 03862 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan); 03863 if (in->cdr && in->_state != AST_STATE_UP) { 03864 ast_cdr_failed(in->cdr); 03865 } 03866 } 03867 *to = 0; 03868 return NULL; 03869 } 03870 03871 /* Poll for events from both the incoming channel as well as any outgoing channels */ 03872 winner = ast_waitfor_n(watchers, pos, to); 03873 03874 /* Service all of the outgoing channels */ 03875 for (o = start; o; o = o->call_next) { 03876 /* We go with a fixed buffer here instead of using ast_strdupa. Using 03877 * ast_strdupa in a loop like this one can cause a stack overflow 03878 */ 03879 char ochan_name[AST_CHANNEL_NAME]; 03880 03881 if (o->chan) { 03882 ast_channel_lock(o->chan); 03883 ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); 03884 ast_channel_unlock(o->chan); 03885 } 03886 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 03887 if (!peer) { 03888 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 03889 if (!o->block_connected_update) { 03890 if (o->pending_connected_update) { 03891 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { 03892 ast_channel_update_connected_line(in, &o->connected, NULL); 03893 } 03894 } else if (!o->dial_callerid_absent) { 03895 ast_channel_lock(o->chan); 03896 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); 03897 ast_channel_unlock(o->chan); 03898 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; 03899 if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) { 03900 ast_channel_update_connected_line(in, &connected_caller, NULL); 03901 } 03902 ast_party_connected_line_free(&connected_caller); 03903 } 03904 } 03905 if (o->aoc_s_rate_list) { 03906 size_t encoded_size; 03907 struct ast_aoc_encoded *encoded; 03908 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) { 03909 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size); 03910 ast_aoc_destroy_encoded(encoded); 03911 } 03912 } 03913 peer = o; 03914 } 03915 } else if (o->chan && (o->chan == winner)) { 03916 03917 ast_copy_string(on, o->member->interface, sizeof(on)); 03918 ast_copy_string(membername, o->member->membername, sizeof(membername)); 03919 03920 /* Before processing channel, go ahead and check for forwarding */ 03921 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 03922 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); 03923 numnochan++; 03924 do_hang(o); 03925 winner = NULL; 03926 continue; 03927 } else if (!ast_strlen_zero(o->chan->call_forward)) { 03928 struct ast_channel *original = o->chan; 03929 char tmpchan[256]; 03930 char *stuff; 03931 char *tech; 03932 03933 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 03934 if ((stuff = strchr(tmpchan, '/'))) { 03935 *stuff++ = '\0'; 03936 tech = tmpchan; 03937 } else { 03938 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 03939 stuff = tmpchan; 03940 tech = "Local"; 03941 } 03942 if (!strcasecmp(tech, "Local")) { 03943 /* 03944 * Drop the connected line update block for local channels since 03945 * this is going to run dialplan and the user can change his 03946 * mind about what connected line information he wants to send. 03947 */ 03948 o->block_connected_update = 0; 03949 } 03950 03951 ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL); 03952 03953 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); 03954 /* Setup parameters */ 03955 o->chan = ast_request(tech, in->nativeformats, in, stuff, &status); 03956 if (!o->chan) { 03957 ast_log(LOG_NOTICE, 03958 "Forwarding failed to create channel to dial '%s/%s'\n", 03959 tech, stuff); 03960 o->stillgoing = 0; 03961 numnochan++; 03962 } else { 03963 ast_channel_lock_both(o->chan, original); 03964 ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting); 03965 ast_channel_unlock(o->chan); 03966 ast_channel_unlock(original); 03967 03968 ast_channel_lock_both(o->chan, in); 03969 ast_channel_inherit_variables(in, o->chan); 03970 ast_channel_datastore_inherit(in, o->chan); 03971 03972 if (o->pending_connected_update) { 03973 /* 03974 * Re-seed the callattempt's connected line information with 03975 * previously acquired connected line info from the queued 03976 * channel. The previously acquired connected line info could 03977 * have been set through the CONNECTED_LINE dialplan function. 03978 */ 03979 o->pending_connected_update = 0; 03980 ast_party_connected_line_copy(&o->connected, &in->connected); 03981 } 03982 03983 ast_string_field_set(o->chan, accountcode, in->accountcode); 03984 03985 if (!o->chan->redirecting.from.number.valid 03986 || ast_strlen_zero(o->chan->redirecting.from.number.str)) { 03987 /* 03988 * The call was not previously redirected so it is 03989 * now redirected from this number. 03990 */ 03991 ast_party_number_free(&o->chan->redirecting.from.number); 03992 ast_party_number_init(&o->chan->redirecting.from.number); 03993 o->chan->redirecting.from.number.valid = 1; 03994 o->chan->redirecting.from.number.str = 03995 ast_strdup(S_OR(in->macroexten, in->exten)); 03996 } 03997 03998 o->chan->dialed.transit_network_select = in->dialed.transit_network_select; 03999 04000 o->dial_callerid_absent = !o->chan->caller.id.number.valid 04001 || ast_strlen_zero(o->chan->caller.id.number.str); 04002 ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller); 04003 04004 ast_channel_unlock(in); 04005 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL 04006 && !o->block_connected_update) { 04007 struct ast_party_redirecting redirecting; 04008 04009 /* 04010 * Redirecting updates to the caller make sense only on single 04011 * call at a time strategies. 04012 * 04013 * We must unlock o->chan before calling 04014 * ast_channel_redirecting_macro, because we put o->chan into 04015 * autoservice there. That is pretty much a guaranteed 04016 * deadlock. This is why the handling of o->chan's lock may 04017 * seem a bit unusual here. 04018 */ 04019 ast_party_redirecting_init(&redirecting); 04020 ast_party_redirecting_copy(&redirecting, &o->chan->redirecting); 04021 ast_channel_unlock(o->chan); 04022 if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) { 04023 ast_channel_update_redirecting(in, &redirecting, NULL); 04024 } 04025 ast_party_redirecting_free(&redirecting); 04026 } else { 04027 ast_channel_unlock(o->chan); 04028 } 04029 04030 if (ast_call(o->chan, stuff, 0)) { 04031 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", 04032 tech, stuff); 04033 do_hang(o); 04034 numnochan++; 04035 } 04036 } 04037 /* Hangup the original channel now, in case we needed it */ 04038 ast_hangup(winner); 04039 continue; 04040 } 04041 f = ast_read(winner); 04042 if (f) { 04043 if (f->frametype == AST_FRAME_CONTROL) { 04044 switch (f->subclass.integer) { 04045 case AST_CONTROL_ANSWER: 04046 /* This is our guy if someone answered. */ 04047 if (!peer) { 04048 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 04049 if (!o->block_connected_update) { 04050 if (o->pending_connected_update) { 04051 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { 04052 ast_channel_update_connected_line(in, &o->connected, NULL); 04053 } 04054 } else if (!o->dial_callerid_absent) { 04055 ast_channel_lock(o->chan); 04056 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); 04057 ast_channel_unlock(o->chan); 04058 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; 04059 if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) { 04060 ast_channel_update_connected_line(in, &connected_caller, NULL); 04061 } 04062 ast_party_connected_line_free(&connected_caller); 04063 } 04064 } 04065 if (o->aoc_s_rate_list) { 04066 size_t encoded_size; 04067 struct ast_aoc_encoded *encoded; 04068 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) { 04069 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size); 04070 ast_aoc_destroy_encoded(encoded); 04071 } 04072 } 04073 peer = o; 04074 } 04075 break; 04076 case AST_CONTROL_BUSY: 04077 ast_verb(3, "%s is busy\n", ochan_name); 04078 //if (in->cdr) 04079 // ast_cdr_busy(in->cdr); 04080 do_hang(o); 04081 endtime = (long) time(NULL); 04082 endtime -= starttime; 04083 rna(endtime * 1000, qe, on, membername, 0); 04084 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 04085 if (qe->parent->timeoutrestart) { 04086 start_time_tv = ast_tvnow(); 04087 } 04088 /* Have enough time for a queue member to answer? */ 04089 if (ast_remaining_ms(start_time_tv, orig) > 500) { 04090 ring_one(qe, outgoing, &numbusies); 04091 starttime = (long) time(NULL); 04092 } 04093 } 04094 numbusies++; 04095 break; 04096 case AST_CONTROL_CONGESTION: 04097 ast_verb(3, "%s is circuit-busy\n", ochan_name); 04098 if (in->cdr) 04099 ast_cdr_failed(in->cdr); 04100 endtime = (long) time(NULL); 04101 endtime -= starttime; 04102 rna(endtime * 1000, qe, on, membername, 0); 04103 do_hang(o); 04104 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 04105 if (qe->parent->timeoutrestart) { 04106 start_time_tv = ast_tvnow(); 04107 } 04108 if (ast_remaining_ms(start_time_tv, orig) > 500) { 04109 ring_one(qe, outgoing, &numbusies); 04110 starttime = (long) time(NULL); 04111 } 04112 } 04113 numbusies++; 04114 break; 04115 case AST_CONTROL_RINGING: 04116 ast_verb(3, "%s is ringing\n", ochan_name); 04117 04118 /* Start ring indication when the channel is ringing, if specified */ 04119 if (qe->ring_when_ringing) { 04120 ast_moh_stop(qe->chan); 04121 ast_indicate(qe->chan, AST_CONTROL_RINGING); 04122 } 04123 break; 04124 case AST_CONTROL_OFFHOOK: 04125 /* Ignore going off hook */ 04126 break; 04127 case AST_CONTROL_CONNECTED_LINE: 04128 if (o->block_connected_update) { 04129 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); 04130 break; 04131 } 04132 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 04133 struct ast_party_connected_line connected; 04134 04135 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name); 04136 ast_party_connected_line_set_init(&connected, &o->connected); 04137 ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); 04138 ast_party_connected_line_set(&o->connected, &connected, NULL); 04139 ast_party_connected_line_free(&connected); 04140 o->pending_connected_update = 1; 04141 break; 04142 } 04143 04144 /* 04145 * Prevent using the CallerID from the outgoing channel since we 04146 * got a connected line update from it. 04147 */ 04148 o->dial_callerid_absent = 1; 04149 04150 if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) { 04151 ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); 04152 } 04153 break; 04154 case AST_CONTROL_AOC: 04155 { 04156 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan); 04157 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) { 04158 ast_aoc_destroy_decoded(o->aoc_s_rate_list); 04159 o->aoc_s_rate_list = decoded; 04160 } else { 04161 ast_aoc_destroy_decoded(decoded); 04162 } 04163 } 04164 break; 04165 case AST_CONTROL_REDIRECTING: 04166 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 04167 /* 04168 * Redirecting updates to the caller make sense only on single 04169 * call at a time strategies. 04170 */ 04171 break; 04172 } 04173 if (o->block_connected_update) { 04174 ast_verb(3, "Redirecting update to %s prevented\n", 04175 inchan_name); 04176 break; 04177 } 04178 ast_verb(3, "%s redirecting info has changed, passing it to %s\n", 04179 ochan_name, inchan_name); 04180 if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) { 04181 ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); 04182 } 04183 break; 04184 default: 04185 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer); 04186 break; 04187 } 04188 } 04189 ast_frfree(f); 04190 } else { /* ast_read() returned NULL */ 04191 endtime = (long) time(NULL) - starttime; 04192 rna(endtime * 1000, qe, on, membername, 1); 04193 do_hang(o); 04194 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 04195 if (qe->parent->timeoutrestart) { 04196 start_time_tv = ast_tvnow(); 04197 } 04198 if (ast_remaining_ms(start_time_tv, orig) > 500) { 04199 ring_one(qe, outgoing, &numbusies); 04200 starttime = (long) time(NULL); 04201 } 04202 } 04203 } 04204 } 04205 } 04206 04207 /* If we received an event from the caller, deal with it. */ 04208 if (winner == in) { 04209 f = ast_read(in); 04210 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) { 04211 /* Got hung up */ 04212 *to = -1; 04213 if (f) { 04214 if (f->data.uint32) { 04215 in->hangupcause = f->data.uint32; 04216 } 04217 ast_frfree(f); 04218 } 04219 return NULL; 04220 } 04221 04222 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) { 04223 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer); 04224 *to = 0; 04225 ast_frfree(f); 04226 if (in->cdr && in->_state != AST_STATE_UP) { 04227 ast_cdr_noanswer(in->cdr); 04228 } 04229 return NULL; 04230 } 04231 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) { 04232 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer); 04233 *to = 0; 04234 *digit = f->subclass.integer; 04235 ast_frfree(f); 04236 if (in->cdr && in->_state != AST_STATE_UP) { 04237 ast_cdr_noanswer(in->cdr); 04238 } 04239 return NULL; 04240 } 04241 04242 /* Send the frame from the in channel to all outgoing channels. */ 04243 for (o = start; o; o = o->call_next) { 04244 if (!o->stillgoing || !o->chan) { 04245 /* This outgoing channel has died so don't send the frame to it. */ 04246 continue; 04247 } 04248 switch (f->frametype) { 04249 case AST_FRAME_CONTROL: 04250 switch (f->subclass.integer) { 04251 case AST_CONTROL_CONNECTED_LINE: 04252 if (ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) { 04253 ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen); 04254 } 04255 break; 04256 case AST_CONTROL_REDIRECTING: 04257 if (ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) { 04258 ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen); 04259 } 04260 break; 04261 default: 04262 /* We are not going to do anything with this frame. */ 04263 goto skip_frame; 04264 } 04265 break; 04266 default: 04267 /* We are not going to do anything with this frame. */ 04268 goto skip_frame; 04269 } 04270 } 04271 skip_frame:; 04272 04273 ast_frfree(f); 04274 } 04275 } 04276 04277 /* Make a position announcement, if enabled */ 04278 if (qe->parent->announcefrequency && qe->parent->announce_to_first_user) { 04279 say_position(qe, ringing); 04280 } 04281 04282 /* Make a periodic announcement, if enabled */ 04283 if (qe->parent->periodicannouncefrequency && qe->parent->announce_to_first_user) { 04284 say_periodic_announcement(qe, ringing); 04285 } 04286 04287 if (!*to) { 04288 for (o = start; o; o = o->call_next) { 04289 rna(orig, qe, o->interface, o->member->membername, 1); 04290 } 04291 } 04292 04293 if (in->cdr 04294 && in->_state != AST_STATE_UP 04295 && (!*to || ast_check_hangup(in))) { 04296 ast_cdr_noanswer(in->cdr); 04297 } 04298 04299 #ifdef HAVE_EPOLL 04300 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 04301 if (epollo->chan) 04302 ast_poll_channel_del(in, epollo->chan); 04303 } 04304 #endif 04305 04306 return peer; 04307 }
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
0 | if the caller's turn has arrived | |
-1 | if the caller should exit the queue. |
Definition at line 4426 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, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, update_qe_rule(), and valid_exit().
Referenced by queue_exec().
04427 { 04428 int res = 0; 04429 04430 /* This is the holding pen for callers 2 through maxlen */ 04431 for (;;) { 04432 04433 if (is_our_turn(qe)) 04434 break; 04435 04436 /* If we have timed out, break out */ 04437 if (qe->expire && (time(NULL) >= qe->expire)) { 04438 *reason = QUEUE_TIMEOUT; 04439 break; 04440 } 04441 04442 if (qe->parent->leavewhenempty) { 04443 int status = 0; 04444 04445 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty, 0))) { 04446 *reason = QUEUE_LEAVEEMPTY; 04447 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 04448 leave_queue(qe); 04449 break; 04450 } 04451 } 04452 04453 /* Make a position announcement, if enabled */ 04454 if (qe->parent->announcefrequency && 04455 (res = say_position(qe,ringing))) 04456 break; 04457 04458 /* If we have timed out, break out */ 04459 if (qe->expire && (time(NULL) >= qe->expire)) { 04460 *reason = QUEUE_TIMEOUT; 04461 break; 04462 } 04463 04464 /* Make a periodic announcement, if enabled */ 04465 if (qe->parent->periodicannouncefrequency && 04466 (res = say_periodic_announcement(qe,ringing))) 04467 break; 04468 04469 /* see if we need to move to the next penalty level for this queue */ 04470 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) { 04471 update_qe_rule(qe); 04472 } 04473 04474 /* If we have timed out, break out */ 04475 if (qe->expire && (time(NULL) >= qe->expire)) { 04476 *reason = QUEUE_TIMEOUT; 04477 break; 04478 } 04479 04480 /* Wait a second before checking again */ 04481 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 04482 if (res > 0 && !valid_exit(qe, res)) 04483 res = 0; 04484 else 04485 break; 04486 } 04487 04488 /* If we have timed out, break out */ 04489 if (qe->expire && (time(NULL) >= qe->expire)) { 04490 *reason = QUEUE_TIMEOUT; 04491 break; 04492 } 04493 } 04494 04495 return res; 04496 }
static int word_in_list | ( | const char * | list, | |
const char * | word | |||
) | [static] |
Check if a given word is in a space-delimited list.
list | Space delimited list of words | |
word | The word used to search the list |
Definition at line 7734 of file app_queue.c.
Referenced by complete_queue().
07734 { 07735 int list_len, word_len = strlen(word); 07736 const char *find, *end_find, *end_list; 07737 07738 /* strip whitespace from front */ 07739 while (isspace(*list)) { 07740 list++; 07741 } 07742 07743 while ((find = strstr(list, word))) { 07744 /* beginning of find starts inside another word? */ 07745 if (find != list && *(find - 1) != ' ') { 07746 list = find; 07747 /* strip word from front */ 07748 while (!isspace(*list) && *list != '\0') { 07749 list++; 07750 } 07751 /* strip whitespace from front */ 07752 while (isspace(*list)) { 07753 list++; 07754 } 07755 continue; 07756 } 07757 07758 /* end of find ends inside another word or at very end of list? */ 07759 list_len = strlen(list); 07760 end_find = find + word_len; 07761 end_list = list + list_len; 07762 if (end_find == end_list || *end_find != ' ') { 07763 list = find; 07764 /* strip word from front */ 07765 while (!isspace(*list) && *list != '\0') { 07766 list++; 07767 } 07768 /* strip whitespace from front */ 07769 while (isspace(*list)) { 07770 list++; 07771 } 07772 continue; 07773 } 07774 07775 /* terminating conditions satisfied, word at beginning or separated by ' ' */ 07776 return 1; 07777 } 07778 07779 return 0; 07780 }
char* app = "Queue" [static] |
Definition at line 1035 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 1037 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 1041 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 1045 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 1039 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 1043 of file app_queue.c.
int autofill_default = 1 [static] |
queues.conf [general] option
Definition at line 1060 of file app_queue.c.
struct autopause autopausesmodes[] [static] |
Referenced by autopause2int().
struct ast_cli_entry cli_queue[] [static] |
Definition at line 8779 of file app_queue.c.
struct ast_event_sub* device_state_sub [static] |
Subscription to device state change events.
Definition at line 1069 of file app_queue.c.
struct ast_taskprocessor* devicestate_tps [static] |
Definition at line 1019 of file app_queue.c.
enum queue_result id |
Definition at line 1086 of file app_queue.c.
Referenced by _sip_show_peers(), _skinny_show_devices(), _skinny_show_lines(), ast_cc_extension_monitor_add_dialstring(), frame_trace_helper(), and idemodulator().
int montype_default = 0 [static] |
queues.conf [general] option
Definition at line 1063 of file app_queue.c.
const char* const pm_family = "Queue/PersistentMembers" [static] |
Persistent Members astdb family.
Definition at line 1048 of file app_queue.c.
const char qpm_cmd_usage[] [static] |
"Usage: queue pause member <channel> in <queue> reason <reason>\n"
Definition at line 8770 of file app_queue.c.
const char qsmp_cmd_usage[] [static] |
"Usage: queue set member penalty <channel> from <queue> <penalty>\n"
Definition at line 8776 of file app_queue.c.
struct ast_data_entry queue_data_providers[] [static] |
{ AST_DATA_ENTRY("asterisk/application/queue/list", &queues_data_provider), }
Definition at line 9057 of file app_queue.c.
int queue_debug = 0 [static] |
queues.conf [general] extra debug option
Definition at line 1051 of file app_queue.c.
int queue_persistent_members = 0 [static] |
queues.conf [general] option
Definition at line 1054 of file app_queue.c.
struct { ... } queue_results[] [static] |
Referenced by set_queue_result().
struct ast_datastore_info queue_transfer_info [static] |
{ .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 4684 of file app_queue.c.
struct ast_custom_function queueexists_function [static] |
{ .name = "QUEUE_EXISTS", .read = queue_function_exists, }
Definition at line 7078 of file app_queue.c.
struct ast_custom_function queuemembercount_dep [static] |
{ .name = "QUEUE_MEMBER_COUNT", .read = queue_function_qac_dep, }
Definition at line 7093 of file app_queue.c.
struct ast_custom_function queuemembercount_function [static] |
{ .name = "QUEUE_MEMBER", .read = queue_function_qac, }
Definition at line 7088 of file app_queue.c.
struct ast_custom_function queuememberlist_function [static] |
{ .name = "QUEUE_MEMBER_LIST", .read = queue_function_queuememberlist, }
Definition at line 7103 of file app_queue.c.
struct ast_custom_function queuememberpaused_function [static] |
{ .name = "QUEUE_MEMBER_PAUSED", .read = queue_function_queuememberpaused, }
Definition at line 7119 of file app_queue.c.
struct ast_custom_function queuememberpenalty_function [static] |
{ .name = "QUEUE_MEMBER_PENALTY", .read = queue_function_memberpenalty_read, .write = queue_function_memberpenalty_write, }
Definition at line 7108 of file app_queue.c.
struct ast_custom_function queuememberstatus_function [static] |
{ .name = "QUEUE_MEMBER_STATUS", .read = queue_function_queuememberstatus, }
Definition at line 7114 of file app_queue.c.
struct ao2_container* queues [static] |
Definition at line 1330 of file app_queue.c.
Referenced by __queues_show(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), extension_state_cb(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), leave_queue(), load_module(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_queuememberlist(), queue_function_queuememberpaused(), queue_function_queuememberstatus(), queue_function_queuewaitingcount(), queue_function_var(), queues_data_provider_get(), reload_queue_members(), reload_queues(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().
struct ast_data_handler queues_data_provider [static] |
{ .version = AST_DATA_HANDLER_VERSION, .get = queues_data_provider_get }
Definition at line 9052 of file app_queue.c.
struct ast_custom_function queuevar_function [static] |
{ .name = "QUEUE_VARIABLES", .read = queue_function_var, }
Definition at line 7083 of file app_queue.c.
struct ast_custom_function queuewaitingcount_function [static] |
{ .name = "QUEUE_WAITING_COUNT", .read = queue_function_queuewaitingcount, }
Definition at line 7098 of file app_queue.c.
const char qum_cmd_usage[] [static] |
"Usage: queue unpause member <channel> in <queue> reason <reason>\n"
Definition at line 8773 of file app_queue.c.
int shared_lastcall = 1 [static] |
queues.conf [general] option
Definition at line 1066 of file app_queue.c.
struct strategy strategies[] [static] |
Referenced by int2strat(), and strat2int().
char* text |
Definition at line 1087 of file app_queue.c.
Referenced by _sip_show_peer(), add_required_respheader(), build_reply_digest(), check_auth(), festival_exec(), handle_response(), handle_response_info(), handle_response_message(), iconv_read(), method_match(), parse_sip_options(), peers_data_provider_get(), process_sdp(), reqprep(), set_queue_result(), and sip_new().
int update_cdr = 0 [static] |
queues.conf [general] option
Definition at line 1072 of file app_queue.c.
Referenced by login_exec().
int use_weight = 0 [static] |
queues.conf per-queue weight option
Definition at line 1057 of file app_queue.c.