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) |
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_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) |
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_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 | queuememberpenalty_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 1065 of file app_queue.c.
Referenced by queue_set_param().
#define ANNOUNCEHOLDTIME_ONCE 2 |
Definition at line 1066 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 1081 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 1080 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 1079 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 1078 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 3593 of file app_queue.c.
#define DATA_EXPORT_CALL_QUEUE | ( | MEMBER | ) |
Definition at line 8424 of file app_queue.c.
#define DATA_EXPORT_MEMBER | ( | MEMBER | ) |
Definition at line 8488 of file app_queue.c.
#define DATA_EXPORT_QUEUE_ENT | ( | MEMBER | ) |
Definition at line 8502 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 891 of file app_queue.c.
Referenced by init_queue().
#define DEFAULT_RETRY 5 |
Definition at line 887 of file app_queue.c.
Referenced by init_queue(), and queue_set_param().
#define DEFAULT_TIMEOUT 15 |
Definition at line 888 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 890 of file app_queue.c.
Referenced by destroy_queue(), init_queue(), and queue_set_param().
#define MAX_QUEUE_BUCKETS 53 |
Definition at line 893 of file app_queue.c.
Referenced by load_module().
#define QUEUE_EVENT_VARIABLES 3 |
Definition at line 1067 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 1327 of file app_queue.c.
Referenced by leave_queue(), and try_calling().
#define queue_t_unref | ( | a, | |||
b | ) | queue_unref(a) |
Definition at line 1328 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_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 1329 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 1330 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 889 of file app_queue.c.
Referenced by wait_our_turn().
#define RES_EXISTS (-1) |
Entry already exists
Definition at line 896 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 898 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 899 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 895 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 897 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 836 of file app_queue.c.
00836 { 00837 QUEUE_STRATEGY_RINGALL = 0, 00838 QUEUE_STRATEGY_LEASTRECENT, 00839 QUEUE_STRATEGY_FEWESTCALLS, 00840 QUEUE_STRATEGY_RANDOM, 00841 QUEUE_STRATEGY_RRMEMORY, 00842 QUEUE_STRATEGY_LINEAR, 00843 QUEUE_STRATEGY_WRANDOM, 00844 QUEUE_STRATEGY_RRORDERED, 00845 };
anonymous enum |
Definition at line 847 of file app_queue.c.
00847 { 00848 QUEUE_AUTOPAUSE_OFF = 0, 00849 QUEUE_AUTOPAUSE_ON, 00850 QUEUE_AUTOPAUSE_ALL 00851 };
Definition at line 4401 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 1053 of file app_queue.c.
01053 { 01054 QUEUE_EMPTY_PENALTY = (1 << 0), 01055 QUEUE_EMPTY_PAUSED = (1 << 1), 01056 QUEUE_EMPTY_INUSE = (1 << 2), 01057 QUEUE_EMPTY_RINGING = (1 << 3), 01058 QUEUE_EMPTY_UNAVAILABLE = (1 << 4), 01059 QUEUE_EMPTY_INVALID = (1 << 5), 01060 QUEUE_EMPTY_UNKNOWN = (1 << 6), 01061 QUEUE_EMPTY_WRAPUP = (1 << 7), 01062 };
enum queue_reload_mask |
Definition at line 853 of file app_queue.c.
00853 { 00854 QUEUE_RELOAD_PARAMETERS = (1 << 0), 00855 QUEUE_RELOAD_MEMBER = (1 << 1), 00856 QUEUE_RELOAD_RULES = (1 << 2), 00857 QUEUE_RESET_STATS = (1 << 3), 00858 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL | |
QUEUE_CONTINUE |
Definition at line 937 of file app_queue.c.
00937 { 00938 QUEUE_UNKNOWN = 0, 00939 QUEUE_TIMEOUT = 1, 00940 QUEUE_JOINEMPTY = 2, 00941 QUEUE_LEAVEEMPTY = 3, 00942 QUEUE_JOINUNAVAIL = 4, 00943 QUEUE_LEAVEUNAVAIL = 5, 00944 QUEUE_FULL = 6, 00945 QUEUE_CONTINUE = 7, 00946 };
Definition at line 962 of file app_queue.c.
00962 { 00963 TIMEOUT_PRIORITY_APP, 00964 TIMEOUT_PRIORITY_CONF, 00965 };
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 7224 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, 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().
07225 { 07226 struct call_queue *q; 07227 struct ast_str *out = ast_str_alloca(240); 07228 int found = 0; 07229 time_t now = time(NULL); 07230 struct ao2_iterator queue_iter; 07231 struct ao2_iterator mem_iter; 07232 07233 if (argc != 2 && argc != 3) 07234 return CLI_SHOWUSAGE; 07235 07236 if (argc == 3) { /* specific queue */ 07237 if ((q = load_realtime_queue(argv[2]))) { 07238 queue_t_unref(q, "Done with temporary pointer"); 07239 } 07240 } else if (ast_check_realtime("queues")) { 07241 /* This block is to find any queues which are defined in realtime but 07242 * which have not yet been added to the in-core container 07243 */ 07244 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 07245 char *queuename; 07246 if (cfg) { 07247 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 07248 if ((q = load_realtime_queue(queuename))) { 07249 queue_t_unref(q, "Done with temporary pointer"); 07250 } 07251 } 07252 ast_config_destroy(cfg); 07253 } 07254 } 07255 07256 ao2_lock(queues); 07257 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK); 07258 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07259 float sl; 07260 struct call_queue *realtime_queue = NULL; 07261 07262 ao2_lock(q); 07263 /* This check is to make sure we don't print information for realtime 07264 * queues which have been deleted from realtime but which have not yet 07265 * been deleted from the in-core container 07266 */ 07267 if (q->realtime) { 07268 realtime_queue = load_realtime_queue(q->name); 07269 if (!realtime_queue) { 07270 ao2_unlock(q); 07271 queue_t_unref(q, "Done with iterator"); 07272 continue; 07273 } 07274 queue_t_unref(realtime_queue, "Queue is already in memory"); 07275 } 07276 07277 if (argc == 3 && strcasecmp(q->name, argv[2])) { 07278 ao2_unlock(q); 07279 queue_t_unref(q, "Done with iterator"); 07280 continue; 07281 } 07282 found = 1; 07283 07284 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count); 07285 if (q->maxlen) 07286 ast_str_append(&out, 0, "%d", q->maxlen); 07287 else 07288 ast_str_append(&out, 0, "unlimited"); 07289 sl = 0; 07290 if (q->callscompleted > 0) 07291 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 07292 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds", 07293 int2strat(q->strategy), q->holdtime, q->talktime, q->weight, 07294 q->callscompleted, q->callsabandoned,sl,q->servicelevel); 07295 do_print(s, fd, ast_str_buffer(out)); 07296 if (!ao2_container_count(q->members)) 07297 do_print(s, fd, " No Members"); 07298 else { 07299 struct member *mem; 07300 07301 do_print(s, fd, " Members: "); 07302 mem_iter = ao2_iterator_init(q->members, 0); 07303 while ((mem = ao2_iterator_next(&mem_iter))) { 07304 ast_str_set(&out, 0, " %s", mem->membername); 07305 if (strcasecmp(mem->membername, mem->interface)) { 07306 ast_str_append(&out, 0, " (%s)", mem->interface); 07307 } 07308 if (mem->penalty) 07309 ast_str_append(&out, 0, " with penalty %d", mem->penalty); 07310 ast_str_append(&out, 0, "%s%s%s (%s)", 07311 mem->dynamic ? " (dynamic)" : "", 07312 mem->realtime ? " (realtime)" : "", 07313 mem->paused ? " (paused)" : "", 07314 ast_devstate2str(mem->status)); 07315 if (mem->calls) 07316 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)", 07317 mem->calls, (long) (time(NULL) - mem->lastcall)); 07318 else 07319 ast_str_append(&out, 0, " has taken no calls yet"); 07320 do_print(s, fd, ast_str_buffer(out)); 07321 ao2_ref(mem, -1); 07322 } 07323 ao2_iterator_destroy(&mem_iter); 07324 } 07325 if (!q->head) 07326 do_print(s, fd, " No Callers"); 07327 else { 07328 struct queue_ent *qe; 07329 int pos = 1; 07330 07331 do_print(s, fd, " Callers: "); 07332 for (qe = q->head; qe; qe = qe->next) { 07333 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)", 07334 pos++, qe->chan->name, (long) (now - qe->start) / 60, 07335 (long) (now - qe->start) % 60, qe->prio); 07336 do_print(s, fd, ast_str_buffer(out)); 07337 } 07338 } 07339 do_print(s, fd, ""); /* blank line between entries */ 07340 ao2_unlock(q); 07341 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */ 07342 } 07343 ao2_iterator_destroy(&queue_iter); 07344 ao2_unlock(queues); 07345 if (!found) { 07346 if (argc == 3) 07347 ast_str_set(&out, 0, "No such queue: %s.", argv[2]); 07348 else 07349 ast_str_set(&out, 0, "No queues."); 07350 do_print(s, fd, ast_str_buffer(out)); 07351 } 07352 return CLI_SUCCESS; 07353 }
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 5525 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().
05526 { 05527 struct call_queue *q; 05528 struct member *new_member, *old_member; 05529 int res = RES_NOSUCHQUEUE; 05530 05531 /*! \note Ensure the appropriate realtime queue is loaded. Note that this 05532 * short-circuits if the queue is already in memory. */ 05533 if (!(q = load_realtime_queue(queuename))) 05534 return res; 05535 05536 ao2_lock(q); 05537 if ((old_member = interface_exists(q, interface)) == NULL) { 05538 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) { 05539 new_member->dynamic = 1; 05540 member_add_to_queue(q, new_member); 05541 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 05542 "Queue: %s\r\n" 05543 "Location: %s\r\n" 05544 "MemberName: %s\r\n" 05545 "Membership: %s\r\n" 05546 "Penalty: %d\r\n" 05547 "CallsTaken: %d\r\n" 05548 "LastCall: %d\r\n" 05549 "Status: %d\r\n" 05550 "Paused: %d\r\n", 05551 q->name, new_member->interface, new_member->membername, 05552 "dynamic", 05553 new_member->penalty, new_member->calls, (int) new_member->lastcall, 05554 new_member->status, new_member->paused); 05555 05556 ao2_ref(new_member, -1); 05557 new_member = NULL; 05558 05559 if (dump) 05560 dump_queue_members(q); 05561 05562 res = RES_OKAY; 05563 } else { 05564 res = RES_OUTOFMEMORY; 05565 } 05566 } else { 05567 ao2_ref(old_member, -1); 05568 res = RES_EXISTS; 05569 } 05570 ao2_unlock(q); 05571 queue_t_unref(q, "Expiring temporary reference"); 05572 05573 return res; 05574 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static, read] |
Definition at line 2261 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().
02262 { 02263 struct call_queue *q; 02264 02265 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) { 02266 if (ast_string_field_init(q, 64)) { 02267 queue_t_unref(q, "String field allocation failed"); 02268 return NULL; 02269 } 02270 ast_string_field_set(q, name, queuename); 02271 } 02272 return q; 02273 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
AddQueueMember application.
Definition at line 5957 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().
05958 { 05959 int res=-1; 05960 char *parse, *temppos = NULL; 05961 AST_DECLARE_APP_ARGS(args, 05962 AST_APP_ARG(queuename); 05963 AST_APP_ARG(interface); 05964 AST_APP_ARG(penalty); 05965 AST_APP_ARG(options); 05966 AST_APP_ARG(membername); 05967 AST_APP_ARG(state_interface); 05968 ); 05969 int penalty = 0; 05970 05971 if (ast_strlen_zero(data)) { 05972 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n"); 05973 return -1; 05974 } 05975 05976 parse = ast_strdupa(data); 05977 05978 AST_STANDARD_APP_ARGS(args, parse); 05979 05980 if (ast_strlen_zero(args.interface)) { 05981 args.interface = ast_strdupa(chan->name); 05982 temppos = strrchr(args.interface, '-'); 05983 if (temppos) 05984 *temppos = '\0'; 05985 } 05986 05987 if (!ast_strlen_zero(args.penalty)) { 05988 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) { 05989 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 05990 penalty = 0; 05991 } 05992 } 05993 05994 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) { 05995 case RES_OKAY: 05996 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 05997 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 05998 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 05999 res = 0; 06000 break; 06001 case RES_EXISTS: 06002 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 06003 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 06004 res = 0; 06005 break; 06006 case RES_NOSUCHQUEUE: 06007 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 06008 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 06009 res = 0; 06010 break; 06011 case RES_OUTOFMEMORY: 06012 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 06013 break; 06014 } 06015 06016 return res; 06017 }
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 4505 of file app_queue.c.
References ast_channel_datastore_find().
Referenced by try_calling().
04506 { 04507 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 04508 }
static int autopause2int | ( | const char * | autopause | ) | [static] |
Definition at line 1232 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().
01233 { 01234 int x; 01235 /*This 'double check' that default value is OFF */ 01236 if (ast_strlen_zero(autopause)) 01237 return QUEUE_AUTOPAUSE_OFF; 01238 01239 /*This 'double check' is to ensure old values works */ 01240 if(ast_true(autopause)) 01241 return QUEUE_AUTOPAUSE_ON; 01242 01243 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) { 01244 if (!strcasecmp(autopause, autopausesmodes[x].name)) 01245 return autopausesmodes[x].autopause; 01246 } 01247 01248 /*This 'double check' that default value is OFF */ 01249 return QUEUE_AUTOPAUSE_OFF; 01250 }
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 4331 of file app_queue.c.
References ao2_container_count(), ast_debug, ast_log(), ast_random(), member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_WARNING, queue_ent::max_penalty, call_queue::members, callattempt::metric, queue_ent::min_penalty, 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, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
Referenced by try_calling().
04332 { 04333 /* disregarding penalty on too few members? */ 04334 int membercount = ao2_container_count(q->members); 04335 unsigned char usepenalty = (membercount <= q->penaltymemberslimit) ? 0 : 1; 04336 04337 if (usepenalty) { 04338 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || 04339 (qe->min_penalty && (mem->penalty < qe->min_penalty))) { 04340 return -1; 04341 } 04342 } else { 04343 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n", 04344 membercount, q->penaltymemberslimit); 04345 } 04346 04347 switch (q->strategy) { 04348 case QUEUE_STRATEGY_RINGALL: 04349 /* Everyone equal, except for penalty */ 04350 tmp->metric = mem->penalty * 1000000 * usepenalty; 04351 break; 04352 case QUEUE_STRATEGY_LINEAR: 04353 if (pos < qe->linpos) { 04354 tmp->metric = 1000 + pos; 04355 } else { 04356 if (pos > qe->linpos) 04357 /* Indicate there is another priority */ 04358 qe->linwrapped = 1; 04359 tmp->metric = pos; 04360 } 04361 tmp->metric += mem->penalty * 1000000 * usepenalty; 04362 break; 04363 case QUEUE_STRATEGY_RRORDERED: 04364 case QUEUE_STRATEGY_RRMEMORY: 04365 pos = mem->queuepos; 04366 if (pos < q->rrpos) { 04367 tmp->metric = 1000 + pos; 04368 } else { 04369 if (pos > q->rrpos) 04370 /* Indicate there is another priority */ 04371 q->wrapped = 1; 04372 tmp->metric = pos; 04373 } 04374 tmp->metric += mem->penalty * 1000000 * usepenalty; 04375 break; 04376 case QUEUE_STRATEGY_RANDOM: 04377 tmp->metric = ast_random() % 1000; 04378 tmp->metric += mem->penalty * 1000000 * usepenalty; 04379 break; 04380 case QUEUE_STRATEGY_WRANDOM: 04381 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000); 04382 break; 04383 case QUEUE_STRATEGY_FEWESTCALLS: 04384 tmp->metric = mem->calls; 04385 tmp->metric += mem->penalty * 1000000 * usepenalty; 04386 break; 04387 case QUEUE_STRATEGY_LEASTRECENT: 04388 if (!mem->lastcall) 04389 tmp->metric = 0; 04390 else 04391 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 04392 tmp->metric += mem->penalty * 1000000 * usepenalty; 04393 break; 04394 default: 04395 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 04396 break; 04397 } 04398 return 0; 04399 }
static void callattempt_free | ( | struct callattempt * | doomed | ) | [static] |
Definition at line 2923 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 3140 of file app_queue.c.
References ast_debug, compare_weight(), get_queue_member_status(), callattempt::interface, callattempt::lastcall, callattempt::lastqueue, 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().
03141 { 03142 if (call->member->paused) { 03143 ast_debug(1, "%s paused, can't receive call\n", call->interface); 03144 return 0; 03145 } 03146 03147 if (!qe->parent->ringinuse && !member_status_available(call->member->status)) { 03148 ast_debug(1, "%s not available, can't receive call\n", call->interface); 03149 return 0; 03150 } 03151 03152 if ((call->lastqueue && call->lastqueue->wrapuptime && (time(NULL) - call->lastcall < call->lastqueue->wrapuptime)) 03153 || (!call->lastqueue && qe->parent->wrapuptime && (time(NULL) - call->lastcall < qe->parent->wrapuptime))) { 03154 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 03155 (call->lastqueue ? call->lastqueue->name : qe->parent->name), 03156 call->interface); 03157 return 0; 03158 } 03159 03160 if (use_weight && compare_weight(qe->parent, call->member)) { 03161 ast_debug(1, "Priority queue delaying call to %s:%s\n", 03162 qe->parent->name, call->interface); 03163 return 0; 03164 } 03165 03166 if (!qe->parent->ringinuse) { 03167 if (member_call_pending_set(call->member)) { 03168 ast_debug(1, "%s has another call pending, can't receive call\n", 03169 call->interface); 03170 return 0; 03171 } 03172 03173 /* 03174 * The queue member is available. Get current status to be sure 03175 * because the device state and extension state callbacks may 03176 * not have updated the status yet. 03177 */ 03178 if (!member_status_available(get_queue_member_status(call->member))) { 03179 ast_debug(1, "%s actually not available, can't receive call\n", 03180 call->interface); 03181 member_call_pending_clear(call->member); 03182 return 0; 03183 } 03184 } 03185 03186 return 1; 03187 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 1792 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().
01793 { 01794 q->holdtime = 0; 01795 q->callscompleted = 0; 01796 q->callsabandoned = 0; 01797 q->callscompletedinsl = 0; 01798 q->talktime = 0; 01799 01800 if (q->members) { 01801 struct member *mem; 01802 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01803 while ((mem = ao2_iterator_next(&mem_iter))) { 01804 mem->calls = 0; 01805 mem->lastcall = 0; 01806 ao2_ref(mem, -1); 01807 } 01808 ao2_iterator_destroy(&mem_iter); 01809 } 01810 }
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 7163 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().
07164 { 07165 struct call_queue *q; 07166 struct ao2_iterator queue_iter; 07167 07168 queue_iter = ao2_iterator_init(queues, 0); 07169 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07170 ao2_lock(q); 07171 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) 07172 clear_queue(q); 07173 ao2_unlock(q); 07174 queue_t_unref(q, "Done with iterator"); 07175 } 07176 ao2_iterator_destroy(&queue_iter); 07177 return 0; 07178 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 3003 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().
03004 { 03005 struct call_queue *q; 03006 struct member *mem; 03007 int found = 0; 03008 struct ao2_iterator queue_iter; 03009 03010 queue_iter = ao2_iterator_init(queues, 0); 03011 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 03012 if (q == rq) { /* don't check myself, could deadlock */ 03013 queue_t_unref(q, "Done with iterator"); 03014 continue; 03015 } 03016 ao2_lock(q); 03017 if (q->count && q->members) { 03018 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 03019 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 03020 if (q->weight > rq->weight && q->count >= num_available_members(q)) { 03021 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); 03022 found = 1; 03023 } 03024 ao2_ref(mem, -1); 03025 } 03026 } 03027 ao2_unlock(q); 03028 queue_t_unref(q, "Done with iterator"); 03029 if (found) { 03030 break; 03031 } 03032 } 03033 ao2_iterator_destroy(&queue_iter); 03034 return found; 03035 }
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 7427 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().
07428 { 07429 struct call_queue *q; 07430 char *ret = NULL; 07431 int which = 0; 07432 int wordlen = strlen(word); 07433 struct ao2_iterator queue_iter; 07434 const char *word_list = NULL; 07435 07436 /* for certain commands, already completed items should be left out of 07437 * the list */ 07438 if (word_list_offset && strlen(line) >= word_list_offset) { 07439 word_list = line + word_list_offset; 07440 } 07441 07442 queue_iter = ao2_iterator_init(queues, 0); 07443 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07444 if (!strncasecmp(word, q->name, wordlen) && ++which > state 07445 && (!word_list_offset || !word_in_list(word_list, q->name))) { 07446 ret = ast_strdup(q->name); 07447 queue_t_unref(q, "Done with iterator"); 07448 break; 07449 } 07450 queue_t_unref(q, "Done with iterator"); 07451 } 07452 ao2_iterator_destroy(&queue_iter); 07453 07454 /* Pretend "rules" is at the end of the queues list in certain 07455 * circumstances since it is an alternate command that should be 07456 * tab-completable for "queue show" */ 07457 if (!ret && which == state && !wordlen && !strncmp("queue show", line, 10)) { 07458 ret = ast_strdup("rules"); 07459 } 07460 07461 return ret; 07462 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7889 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
Referenced by handle_queue_add_member().
07890 { 07891 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 07892 switch (pos) { 07893 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 07894 return NULL; 07895 case 4: /* only one possible match, "to" */ 07896 return state == 0 ? ast_strdup("to") : NULL; 07897 case 5: /* <queue> */ 07898 return complete_queue(line, word, pos, state, 0); 07899 case 6: /* only one possible match, "penalty" */ 07900 return state == 0 ? ast_strdup("penalty") : NULL; 07901 case 7: 07902 if (state < 100) { /* 0-99 */ 07903 char *num; 07904 if ((num = ast_malloc(3))) { 07905 sprintf(num, "%d", state); 07906 } 07907 return num; 07908 } else { 07909 return NULL; 07910 } 07911 case 8: /* only one possible match, "as" */ 07912 return state == 0 ? ast_strdup("as") : NULL; 07913 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 07914 return NULL; 07915 default: 07916 return NULL; 07917 } 07918 }
static char* complete_queue_pause_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8111 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_pause_member().
08112 { 08113 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */ 08114 switch (pos) { 08115 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 08116 return NULL; 08117 case 4: /* only one possible match, "queue" */ 08118 return state == 0 ? ast_strdup("queue") : NULL; 08119 case 5: /* <queue> */ 08120 return complete_queue(line, word, pos, state, 0); 08121 case 6: /* "reason" */ 08122 return state == 0 ? ast_strdup("reason") : NULL; 08123 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */ 08124 return NULL; 08125 default: 08126 return NULL; 08127 } 08128 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8019 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().
08020 { 08021 int which = 0; 08022 struct call_queue *q; 08023 struct member *m; 08024 struct ao2_iterator queue_iter; 08025 struct ao2_iterator mem_iter; 08026 int wordlen = strlen(word); 08027 08028 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 08029 if (pos > 5 || pos < 3) 08030 return NULL; 08031 if (pos == 4) /* only one possible match, 'from' */ 08032 return (state == 0 ? ast_strdup("from") : NULL); 08033 08034 if (pos == 5) { /* No need to duplicate code */ 08035 return complete_queue(line, word, pos, state, 0); 08036 } 08037 08038 /* here is the case for 3, <member> */ 08039 queue_iter = ao2_iterator_init(queues, 0); 08040 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 08041 ao2_lock(q); 08042 mem_iter = ao2_iterator_init(q->members, 0); 08043 while ((m = ao2_iterator_next(&mem_iter))) { 08044 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) { 08045 char *tmp; 08046 tmp = ast_strdup(m->interface); 08047 ao2_ref(m, -1); 08048 ao2_iterator_destroy(&mem_iter); 08049 ao2_unlock(q); 08050 queue_t_unref(q, "Done with iterator, returning interface name"); 08051 ao2_iterator_destroy(&queue_iter); 08052 return tmp; 08053 } 08054 ao2_ref(m, -1); 08055 } 08056 ao2_iterator_destroy(&mem_iter); 08057 ao2_unlock(q); 08058 queue_t_unref(q, "Done with iterator"); 08059 } 08060 ao2_iterator_destroy(&queue_iter); 08061 08062 return NULL; 08063 }
static char* complete_queue_rule_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8244 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().
08245 { 08246 int which = 0; 08247 struct rule_list *rl_iter; 08248 int wordlen = strlen(word); 08249 char *ret = NULL; 08250 if (pos != 3) /* Wha? */ { 08251 return NULL; 08252 } 08253 08254 AST_LIST_LOCK(&rule_lists); 08255 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 08256 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) { 08257 ret = ast_strdup(rl_iter->name); 08258 break; 08259 } 08260 } 08261 AST_LIST_UNLOCK(&rule_lists); 08262 08263 return ret; 08264 }
static char* complete_queue_set_member_penalty | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 8181 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_set_member_penalty().
08182 { 08183 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/ 08184 switch (pos) { 08185 case 4: 08186 if (state == 0) { 08187 return ast_strdup("on"); 08188 } else { 08189 return NULL; 08190 } 08191 case 6: 08192 if (state == 0) { 08193 return ast_strdup("in"); 08194 } else { 08195 return NULL; 08196 } 08197 case 7: 08198 return complete_queue(line, word, pos, state, 0); 08199 default: 08200 return NULL; 08201 } 08202 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7464 of file app_queue.c.
References complete_queue().
Referenced by queue_show().
07465 { 07466 if (pos == 2) { 07467 return complete_queue(line, word, pos, state, 0); 07468 } 07469 return NULL; 07470 }
static int compress_char | ( | const char | c | ) | [static] |
Definition at line 1684 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 6054 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().
06055 { 06056 struct penalty_rule *pr_iter; 06057 struct rule_list *rl_iter; 06058 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename; 06059 AST_LIST_LOCK(&rule_lists); 06060 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 06061 if (!strcasecmp(rl_iter->name, tmp)) 06062 break; 06063 } 06064 if (rl_iter) { 06065 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 06066 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr)); 06067 if (!new_pr) { 06068 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n"); 06069 break; 06070 } 06071 new_pr->time = pr_iter->time; 06072 new_pr->max_value = pr_iter->max_value; 06073 new_pr->min_value = pr_iter->min_value; 06074 new_pr->max_relative = pr_iter->max_relative; 06075 new_pr->min_relative = pr_iter->min_relative; 06076 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list); 06077 } 06078 } 06079 AST_LIST_UNLOCK(&rule_lists); 06080 }
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 1652 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().
01653 { 01654 struct member *cur; 01655 01656 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 01657 cur->penalty = penalty; 01658 cur->paused = paused; 01659 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 01660 if (!ast_strlen_zero(state_interface)) 01661 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); 01662 else 01663 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface)); 01664 if (!ast_strlen_zero(membername)) 01665 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 01666 else 01667 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 01668 if (!strchr(cur->interface, '/')) 01669 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 01670 if (!strncmp(cur->state_interface, "hint:", 5)) { 01671 char *tmp = ast_strdupa(cur->state_interface), *context = tmp; 01672 char *exten = strsep(&context, "@") + 5; 01673 01674 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten)); 01675 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context)); 01676 } 01677 cur->status = get_queue_member_status(cur); 01678 } 01679 01680 return cur; 01681 }
static void destroy_queue | ( | void * | obj | ) | [static] |
Free queue's member list then its string fields.
Definition at line 2247 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().
02248 { 02249 struct call_queue *q = obj; 02250 int i; 02251 02252 free_members(q, 1); 02253 ast_string_field_free_memory(q); 02254 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 02255 if (q->sound_periodicannounce[i]) 02256 free(q->sound_periodicannounce[i]); 02257 } 02258 ao2_ref(q->members, -1); 02259 }
static void device_state_cb | ( | const struct ast_event * | event, | |
void * | unused | |||
) | [static] |
Definition at line 1550 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().
01551 { 01552 enum ast_device_state state; 01553 const char *device; 01554 struct statechange *sc; 01555 size_t datapsize; 01556 01557 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); 01558 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); 01559 01560 if (ast_strlen_zero(device)) { 01561 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n"); 01562 return; 01563 } 01564 datapsize = sizeof(*sc) + strlen(device) + 1; 01565 if (!(sc = ast_calloc(1, datapsize))) { 01566 ast_log(LOG_ERROR, "failed to calloc a state change struct\n"); 01567 return; 01568 } 01569 sc->state = state; 01570 strcpy(sc->dev, device); 01571 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) { 01572 ast_free(sc); 01573 } 01574 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 3038 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry(), and wait_for_answer().
03039 { 03040 o->stillgoing = 0; 03041 ast_hangup(o->chan); 03042 o->chan = NULL; 03043 }
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 7210 of file app_queue.c.
References ast_cli(), and astman_append().
Referenced by __queues_show().
07211 { 07212 if (s) 07213 astman_append(s, "%s\r\n", str); 07214 else 07215 ast_cli(fd, "%s\n", str); 07216 }
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 5425 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().
05426 { 05427 struct member *cur_member; 05428 struct ast_str *value; 05429 struct ao2_iterator mem_iter; 05430 05431 if (!pm_queue) { 05432 return; 05433 } 05434 05435 /* 4K is a reasonable default for most applications, but we grow to 05436 * accommodate more if necessary. */ 05437 if (!(value = ast_str_create(4096))) { 05438 return; 05439 } 05440 05441 mem_iter = ao2_iterator_init(pm_queue->members, 0); 05442 while ((cur_member = ao2_iterator_next(&mem_iter))) { 05443 if (!cur_member->dynamic) { 05444 ao2_ref(cur_member, -1); 05445 continue; 05446 } 05447 05448 ast_str_append(&value, 0, "%s%s;%d;%d;%s;%s", 05449 ast_str_strlen(value) ? "|" : "", 05450 cur_member->interface, 05451 cur_member->penalty, 05452 cur_member->paused, 05453 cur_member->membername, 05454 cur_member->state_interface); 05455 05456 ao2_ref(cur_member, -1); 05457 } 05458 ao2_iterator_destroy(&mem_iter); 05459 05460 if (ast_str_strlen(value) && !cur_member) { 05461 if (ast_db_put(pm_family, pm_queue->name, ast_str_buffer(value))) 05462 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 05463 } else { 05464 /* Delete the entry if the queue is empty or there is an error */ 05465 ast_db_del(pm_family, pm_queue->name); 05466 } 05467 05468 ast_free(value); 05469 }
static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 4553 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().
04554 { 04555 struct queue_end_bridge *qeb = data; 04556 struct call_queue *q = qeb->q; 04557 struct ast_channel *chan = qeb->chan; 04558 04559 if (ao2_ref(qeb, -1) == 1) { 04560 set_queue_variables(q, chan); 04561 /* This unrefs the reference we made in try_calling when we allocated qeb */ 04562 queue_t_unref(q, "Expire bridge_config reference"); 04563 } 04564 }
static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, | |
struct ast_channel * | originator, | |||
struct ast_channel * | terminator | |||
) | [static] |
Definition at line 4546 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, and ast_bridge_config::end_bridge_callback_data.
Referenced by try_calling().
04547 { 04548 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data; 04549 ao2_ref(qeb, +1); 04550 qeb->chan = originator; 04551 }
static int extension_state_cb | ( | char * | context, | |
char * | exten, | |||
enum ast_extension_states | state, | |||
void * | data | |||
) | [static] |
Definition at line 1608 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().
01609 { 01610 struct ao2_iterator miter, qiter; 01611 struct member *m; 01612 struct call_queue *q; 01613 int found = 0, device_state = extensionstate2devicestate(state); 01614 01615 qiter = ao2_iterator_init(queues, 0); 01616 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) { 01617 ao2_lock(q); 01618 01619 miter = ao2_iterator_init(q->members, 0); 01620 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01621 if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) { 01622 update_status(q, m, device_state); 01623 ao2_ref(m, -1); 01624 found = 1; 01625 break; 01626 } 01627 } 01628 ao2_iterator_destroy(&miter); 01629 01630 ao2_unlock(q); 01631 queue_t_unref(q, "Done with iterator"); 01632 } 01633 ao2_iterator_destroy(&qiter); 01634 01635 if (found) { 01636 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state)); 01637 } else { 01638 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", 01639 exten, context, device_state, ast_devstate2str(device_state)); 01640 } 01641 01642 return 0; 01643 }
static int extensionstate2devicestate | ( | int | state | ) | [static] |
Helper function which converts from extension state to device state values.
Definition at line 1577 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().
01578 { 01579 switch (state) { 01580 case AST_EXTENSION_NOT_INUSE: 01581 state = AST_DEVICE_NOT_INUSE; 01582 break; 01583 case AST_EXTENSION_INUSE: 01584 state = AST_DEVICE_INUSE; 01585 break; 01586 case AST_EXTENSION_BUSY: 01587 state = AST_DEVICE_BUSY; 01588 break; 01589 case AST_EXTENSION_RINGING: 01590 state = AST_DEVICE_RINGING; 01591 break; 01592 case AST_EXTENSION_ONHOLD: 01593 state = AST_DEVICE_ONHOLD; 01594 break; 01595 case AST_EXTENSION_UNAVAILABLE: 01596 state = AST_DEVICE_UNAVAILABLE; 01597 break; 01598 case AST_EXTENSION_REMOVED: 01599 case AST_EXTENSION_DEACTIVATED: 01600 default: 01601 state = AST_DEVICE_INVALID; 01602 break; 01603 } 01604 01605 return state; 01606 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static, read] |
find the entry with the best metric, or NULL
Definition at line 3358 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
Referenced by ring_one(), store_next_lin(), and store_next_rr().
03359 { 03360 struct callattempt *best = NULL, *cur; 03361 03362 for (cur = outgoing; cur; cur = cur->q_next) { 03363 if (cur->stillgoing && /* Not already done */ 03364 !cur->chan && /* Isn't already going */ 03365 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 03366 best = cur; 03367 } 03368 } 03369 03370 return best; 03371 }
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 2285 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().
02286 { 02287 struct ast_variable *v; 02288 struct call_queue *q, tmpq = { 02289 .name = queuename, 02290 }; 02291 struct member *m; 02292 struct ao2_iterator mem_iter; 02293 char *interface = NULL; 02294 const char *tmp_name; 02295 char *tmp; 02296 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 02297 02298 /* Static queues override realtime. */ 02299 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) { 02300 ao2_lock(q); 02301 if (!q->realtime) { 02302 if (q->dead) { 02303 ao2_unlock(q); 02304 queue_t_unref(q, "Queue is dead; can't return it"); 02305 return NULL; 02306 } else { 02307 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 02308 ao2_unlock(q); 02309 return q; 02310 } 02311 } 02312 } else if (!member_config) 02313 /* Not found in the list, and it's not realtime ... */ 02314 return NULL; 02315 02316 /* Check if queue is defined in realtime. */ 02317 if (!queue_vars) { 02318 /* Delete queue from in-core list if it has been deleted in realtime. */ 02319 if (q) { 02320 /*! \note Hmm, can't seem to distinguish a DB failure from a not 02321 found condition... So we might delete an in-core queue 02322 in case of DB failure. */ 02323 ast_debug(1, "Queue %s not found in realtime.\n", queuename); 02324 02325 q->dead = 1; 02326 /* Delete if unused (else will be deleted when last caller leaves). */ 02327 queues_t_unlink(queues, q, "Unused; removing from container"); 02328 ao2_unlock(q); 02329 queue_t_unref(q, "Queue is dead; can't return it"); 02330 } 02331 return NULL; 02332 } 02333 02334 /* Create a new queue if an in-core entry does not exist yet. */ 02335 if (!q) { 02336 struct ast_variable *tmpvar = NULL; 02337 if (!(q = alloc_queue(queuename))) 02338 return NULL; 02339 ao2_lock(q); 02340 clear_queue(q); 02341 q->realtime = 1; 02342 /*Before we initialize the queue, we need to set the strategy, so that linear strategy 02343 * will allocate the members properly 02344 */ 02345 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { 02346 if (!strcasecmp(tmpvar->name, "strategy")) { 02347 q->strategy = strat2int(tmpvar->value); 02348 if (q->strategy < 0) { 02349 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 02350 tmpvar->value, q->name); 02351 q->strategy = QUEUE_STRATEGY_RINGALL; 02352 } 02353 break; 02354 } 02355 } 02356 /* We traversed all variables and didn't find a strategy */ 02357 if (!tmpvar) 02358 q->strategy = QUEUE_STRATEGY_RINGALL; 02359 queues_t_link(queues, q, "Add queue to container"); 02360 } 02361 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 02362 02363 memset(tmpbuf, 0, sizeof(tmpbuf)); 02364 for (v = queue_vars; v; v = v->next) { 02365 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 02366 if (strchr(v->name, '_')) { 02367 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 02368 tmp_name = tmpbuf; 02369 tmp = tmpbuf; 02370 while ((tmp = strchr(tmp, '_'))) 02371 *tmp++ = '-'; 02372 } else 02373 tmp_name = v->name; 02374 02375 /* NULL values don't get returned from realtime; blank values should 02376 * still get set. If someone doesn't want a value to be set, they 02377 * should set the realtime column to NULL, not blank. */ 02378 queue_set_param(q, tmp_name, v->value, -1, 0); 02379 } 02380 02381 /* Temporarily set realtime members dead so we can detect deleted ones. */ 02382 mem_iter = ao2_iterator_init(q->members, 0); 02383 while ((m = ao2_iterator_next(&mem_iter))) { 02384 if (m->realtime) 02385 m->dead = 1; 02386 ao2_ref(m, -1); 02387 } 02388 ao2_iterator_destroy(&mem_iter); 02389 02390 while ((interface = ast_category_browse(member_config, interface))) { 02391 rt_handle_member_record(q, interface, 02392 ast_variable_retrieve(member_config, interface, "uniqueid"), 02393 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface), 02394 ast_variable_retrieve(member_config, interface, "penalty"), 02395 ast_variable_retrieve(member_config, interface, "paused"), 02396 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface)); 02397 } 02398 02399 /* Delete all realtime members that have been deleted in DB. */ 02400 mem_iter = ao2_iterator_init(q->members, 0); 02401 while ((m = ao2_iterator_next(&mem_iter))) { 02402 if (m->dead) { 02403 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 02404 member_remove_from_queue(q, m); 02405 } 02406 ao2_ref(m, -1); 02407 } 02408 ao2_iterator_destroy(&mem_iter); 02409 02410 ao2_unlock(q); 02411 02412 return q; 02413 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Iterate through queue's member list and delete them.
Definition at line 2231 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().
02232 { 02233 /* Free non-dynamic members */ 02234 struct member *cur; 02235 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 02236 02237 while ((cur = ao2_iterator_next(&mem_iter))) { 02238 if (all || !cur->dynamic) { 02239 member_remove_from_queue(q, cur); 02240 } 02241 ao2_ref(cur, -1); 02242 } 02243 ao2_iterator_destroy(&mem_iter); 02244 }
static int get_member_penalty | ( | char * | queuename, | |
char * | interface | |||
) | [static] |
Definition at line 5702 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().
05703 { 05704 int foundqueue = 0, penalty; 05705 struct call_queue *q, tmpq = { 05706 .name = queuename, 05707 }; 05708 struct member *mem; 05709 05710 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) { 05711 foundqueue = 1; 05712 ao2_lock(q); 05713 if ((mem = interface_exists(q, interface))) { 05714 penalty = mem->penalty; 05715 ao2_ref(mem, -1); 05716 ao2_unlock(q); 05717 queue_t_unref(q, "Search complete"); 05718 return penalty; 05719 } 05720 ao2_unlock(q); 05721 queue_t_unref(q, "Search complete"); 05722 } 05723 05724 /* some useful debuging */ 05725 if (foundqueue) 05726 ast_log (LOG_ERROR, "Invalid queuename\n"); 05727 else 05728 ast_log (LOG_ERROR, "Invalid interface\n"); 05729 05730 return RESULT_FAILURE; 05731 }
static int get_member_status | ( | struct call_queue * | q, | |
int | max_penalty, | |||
int | min_penalty, | |||
enum empty_conditions | conditions | |||
) | [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 1400 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::status, and call_queue::wrapuptime.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
01401 { 01402 struct member *member; 01403 struct ao2_iterator mem_iter; 01404 01405 ao2_lock(q); 01406 mem_iter = ao2_iterator_init(q->members, 0); 01407 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) { 01408 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) { 01409 if (conditions & QUEUE_EMPTY_PENALTY) { 01410 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty); 01411 continue; 01412 } 01413 } 01414 01415 switch (member->status) { 01416 case AST_DEVICE_INVALID: 01417 if (conditions & QUEUE_EMPTY_INVALID) { 01418 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername); 01419 break; 01420 } 01421 goto default_case; 01422 case AST_DEVICE_UNAVAILABLE: 01423 if (conditions & QUEUE_EMPTY_UNAVAILABLE) { 01424 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername); 01425 break; 01426 } 01427 goto default_case; 01428 case AST_DEVICE_INUSE: 01429 if (conditions & QUEUE_EMPTY_INUSE) { 01430 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername); 01431 break; 01432 } 01433 goto default_case; 01434 case AST_DEVICE_RINGING: 01435 if (conditions & QUEUE_EMPTY_RINGING) { 01436 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername); 01437 break; 01438 } 01439 goto default_case; 01440 case AST_DEVICE_UNKNOWN: 01441 if (conditions & QUEUE_EMPTY_UNKNOWN) { 01442 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername); 01443 break; 01444 } 01445 /* Fall-through */ 01446 default: 01447 default_case: 01448 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) { 01449 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername); 01450 break; 01451 } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) { 01452 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); 01453 break; 01454 } else { 01455 ao2_ref(member, -1); 01456 ao2_iterator_destroy(&mem_iter); 01457 ao2_unlock(q); 01458 ast_debug(4, "%s is available.\n", member->membername); 01459 return 0; 01460 } 01461 break; 01462 } 01463 } 01464 ao2_iterator_destroy(&mem_iter); 01465 01466 ao2_unlock(q); 01467 return -1; 01468 }
static int get_queue_member_status | ( | struct member * | cur | ) | [static] |
Return the current state of a member.
Definition at line 1646 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().
01647 { 01648 return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten)); 01649 }
static char* handle_queue_add_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7945 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.
07946 { 07947 const char *queuename, *interface, *membername = NULL, *state_interface = NULL; 07948 int penalty; 07949 07950 switch ( cmd ) { 07951 case CLI_INIT: 07952 e->command = "queue add member"; 07953 e->usage = 07954 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n" 07955 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n"; 07956 return NULL; 07957 case CLI_GENERATE: 07958 return complete_queue_add_member(a->line, a->word, a->pos, a->n); 07959 } 07960 07961 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) { 07962 return CLI_SHOWUSAGE; 07963 } else if (strcmp(a->argv[4], "to")) { 07964 return CLI_SHOWUSAGE; 07965 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) { 07966 return CLI_SHOWUSAGE; 07967 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) { 07968 return CLI_SHOWUSAGE; 07969 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) { 07970 return CLI_SHOWUSAGE; 07971 } 07972 07973 queuename = a->argv[5]; 07974 interface = a->argv[3]; 07975 if (a->argc >= 8) { 07976 if (sscanf(a->argv[7], "%30d", &penalty) == 1) { 07977 if (penalty < 0) { 07978 ast_cli(a->fd, "Penalty must be >= 0\n"); 07979 penalty = 0; 07980 } 07981 } else { 07982 ast_cli(a->fd, "Penalty must be an integer >= 0\n"); 07983 penalty = 0; 07984 } 07985 } else { 07986 penalty = 0; 07987 } 07988 07989 if (a->argc >= 10) { 07990 membername = a->argv[9]; 07991 } 07992 07993 if (a->argc >= 12) { 07994 state_interface = a->argv[11]; 07995 } 07996 07997 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) { 07998 case RES_OKAY: 07999 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 08000 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 08001 return CLI_SUCCESS; 08002 case RES_EXISTS: 08003 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 08004 return CLI_FAILURE; 08005 case RES_NOSUCHQUEUE: 08006 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 08007 return CLI_FAILURE; 08008 case RES_OUTOFMEMORY: 08009 ast_cli(a->fd, "Out of memory\n"); 08010 return CLI_FAILURE; 08011 case RES_NOT_DYNAMIC: 08012 ast_cli(a->fd, "Member not dynamic\n"); 08013 return CLI_FAILURE; 08014 default: 08015 return CLI_FAILURE; 08016 } 08017 }
static char* handle_queue_pause_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8130 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.
08131 { 08132 const char *queuename, *interface, *reason; 08133 int paused; 08134 08135 switch (cmd) { 08136 case CLI_INIT: 08137 e->command = "queue {pause|unpause} member"; 08138 e->usage = 08139 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n" 08140 " Pause or unpause a queue member. Not specifying a particular queue\n" 08141 " will pause or unpause a member across all queues to which the member\n" 08142 " belongs.\n"; 08143 return NULL; 08144 case CLI_GENERATE: 08145 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n); 08146 } 08147 08148 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) { 08149 return CLI_SHOWUSAGE; 08150 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) { 08151 return CLI_SHOWUSAGE; 08152 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) { 08153 return CLI_SHOWUSAGE; 08154 } 08155 08156 08157 interface = a->argv[3]; 08158 queuename = a->argc >= 6 ? a->argv[5] : NULL; 08159 reason = a->argc == 8 ? a->argv[7] : NULL; 08160 paused = !strcasecmp(a->argv[1], "pause"); 08161 08162 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) { 08163 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface); 08164 if (!ast_strlen_zero(queuename)) 08165 ast_cli(a->fd, " in queue '%s'", queuename); 08166 if (!ast_strlen_zero(reason)) 08167 ast_cli(a->fd, " for reason '%s'", reason); 08168 ast_cli(a->fd, "\n"); 08169 return CLI_SUCCESS; 08170 } else { 08171 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface); 08172 if (!ast_strlen_zero(queuename)) 08173 ast_cli(a->fd, " in queue '%s'", queuename); 08174 if (!ast_strlen_zero(reason)) 08175 ast_cli(a->fd, " for reason '%s'", reason); 08176 ast_cli(a->fd, "\n"); 08177 return CLI_FAILURE; 08178 } 08179 }
static char* handle_queue_reload | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8339 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.
08340 { 08341 struct ast_flags mask = {0,}; 08342 int i; 08343 08344 switch (cmd) { 08345 case CLI_INIT: 08346 e->command = "queue reload {parameters|members|rules|all}"; 08347 e->usage = 08348 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n" 08349 "Reload queues. If <queuenames> are specified, only reload information pertaining\n" 08350 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n" 08351 "specified in order to know what information to reload. Below is an explanation\n" 08352 "of each of these qualifiers.\n" 08353 "\n" 08354 "\t'members' - reload queue members from queues.conf\n" 08355 "\t'parameters' - reload all queue options except for queue members\n" 08356 "\t'rules' - reload the queuerules.conf file\n" 08357 "\t'all' - reload queue rules, parameters, and members\n" 08358 "\n" 08359 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n" 08360 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n" 08361 "one queue is specified when using this command, reloading queue rules may cause\n" 08362 "other queues to be affected\n"; 08363 return NULL; 08364 case CLI_GENERATE: 08365 if (a->pos >= 3) { 08366 /* find the point at which the list of queue names starts */ 08367 const char *command_end = a->line + strlen("queue reload "); 08368 command_end = strchr(command_end, ' '); 08369 if (!command_end) { 08370 command_end = a->line + strlen(a->line); 08371 } 08372 return complete_queue(a->line, a->word, a->pos, a->n, command_end - a->line); 08373 } else { 08374 return NULL; 08375 } 08376 } 08377 08378 if (a->argc < 3) 08379 return CLI_SHOWUSAGE; 08380 08381 if (!strcasecmp(a->argv[2], "rules")) { 08382 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 08383 } else if (!strcasecmp(a->argv[2], "members")) { 08384 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 08385 } else if (!strcasecmp(a->argv[2], "parameters")) { 08386 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 08387 } else if (!strcasecmp(a->argv[2], "all")) { 08388 ast_set_flag(&mask, AST_FLAGS_ALL); 08389 } 08390 08391 if (a->argc == 3) { 08392 reload_handler(1, &mask, NULL); 08393 return CLI_SUCCESS; 08394 } 08395 08396 for (i = 3; i < a->argc; ++i) { 08397 reload_handler(1, &mask, a->argv[i]); 08398 } 08399 08400 return CLI_SUCCESS; 08401 }
static char* handle_queue_remove_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8065 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.
08066 { 08067 const char *queuename, *interface; 08068 08069 switch (cmd) { 08070 case CLI_INIT: 08071 e->command = "queue remove member"; 08072 e->usage = 08073 "Usage: queue remove member <channel> from <queue>\n" 08074 " Remove a specific channel from a queue.\n"; 08075 return NULL; 08076 case CLI_GENERATE: 08077 return complete_queue_remove_member(a->line, a->word, a->pos, a->n); 08078 } 08079 08080 if (a->argc != 6) { 08081 return CLI_SHOWUSAGE; 08082 } else if (strcmp(a->argv[4], "from")) { 08083 return CLI_SHOWUSAGE; 08084 } 08085 08086 queuename = a->argv[5]; 08087 interface = a->argv[3]; 08088 08089 switch (remove_from_queue(queuename, interface)) { 08090 case RES_OKAY: 08091 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 08092 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 08093 return CLI_SUCCESS; 08094 case RES_EXISTS: 08095 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 08096 return CLI_FAILURE; 08097 case RES_NOSUCHQUEUE: 08098 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 08099 return CLI_FAILURE; 08100 case RES_OUTOFMEMORY: 08101 ast_cli(a->fd, "Out of memory\n"); 08102 return CLI_FAILURE; 08103 case RES_NOT_DYNAMIC: 08104 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename); 08105 return CLI_FAILURE; 08106 default: 08107 return CLI_FAILURE; 08108 } 08109 }
static char* handle_queue_reset | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8300 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.
08301 { 08302 struct ast_flags mask = {QUEUE_RESET_STATS,}; 08303 int i; 08304 08305 switch (cmd) { 08306 case CLI_INIT: 08307 e->command = "queue reset stats"; 08308 e->usage = 08309 "Usage: queue reset stats [<queuenames>]\n" 08310 "\n" 08311 "Issuing this command will reset statistics for\n" 08312 "<queuenames>, or for all queues if no queue is\n" 08313 "specified.\n"; 08314 return NULL; 08315 case CLI_GENERATE: 08316 if (a->pos >= 3) { 08317 return complete_queue(a->line, a->word, a->pos, a->n, 17); 08318 } else { 08319 return NULL; 08320 } 08321 } 08322 08323 if (a->argc < 3) { 08324 return CLI_SHOWUSAGE; 08325 } 08326 08327 if (a->argc == 3) { 08328 reload_handler(1, &mask, NULL); 08329 return CLI_SUCCESS; 08330 } 08331 08332 for (i = 3; i < a->argc; ++i) { 08333 reload_handler(1, &mask, a->argv[i]); 08334 } 08335 08336 return CLI_SUCCESS; 08337 }
static char* handle_queue_rule_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8266 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.
08267 { 08268 const char *rule; 08269 struct rule_list *rl_iter; 08270 struct penalty_rule *pr_iter; 08271 switch (cmd) { 08272 case CLI_INIT: 08273 e->command = "queue show rules"; 08274 e->usage = 08275 "Usage: queue show rules [rulename]\n" 08276 " Show the list of rules associated with rulename. If no\n" 08277 " rulename is specified, list all rules defined in queuerules.conf\n"; 08278 return NULL; 08279 case CLI_GENERATE: 08280 return complete_queue_rule_show(a->line, a->word, a->pos, a->n); 08281 } 08282 08283 if (a->argc != 3 && a->argc != 4) 08284 return CLI_SHOWUSAGE; 08285 08286 rule = a->argc == 4 ? a->argv[3] : ""; 08287 AST_LIST_LOCK(&rule_lists); 08288 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 08289 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) { 08290 ast_cli(a->fd, "Rule: %s\n", rl_iter->name); 08291 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 08292 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); 08293 } 08294 } 08295 } 08296 AST_LIST_UNLOCK(&rule_lists); 08297 return CLI_SUCCESS; 08298 }
static char* handle_queue_set_member_penalty | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 8204 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.
08205 { 08206 const char *queuename = NULL, *interface; 08207 int penalty = 0; 08208 08209 switch (cmd) { 08210 case CLI_INIT: 08211 e->command = "queue set penalty"; 08212 e->usage = 08213 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n" 08214 " Set a member's penalty in the queue specified. If no queue is specified\n" 08215 " then that interface's penalty is set in all queues to which that interface is a member\n"; 08216 return NULL; 08217 case CLI_GENERATE: 08218 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n); 08219 } 08220 08221 if (a->argc != 6 && a->argc != 8) { 08222 return CLI_SHOWUSAGE; 08223 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) { 08224 return CLI_SHOWUSAGE; 08225 } 08226 08227 if (a->argc == 8) 08228 queuename = a->argv[7]; 08229 interface = a->argv[5]; 08230 penalty = atoi(a->argv[3]); 08231 08232 switch (set_member_penalty(queuename, interface, penalty)) { 08233 case RESULT_SUCCESS: 08234 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); 08235 return CLI_SUCCESS; 08236 case RESULT_FAILURE: 08237 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); 08238 return CLI_FAILURE; 08239 default: 08240 return CLI_FAILURE; 08241 } 08242 }
static int handle_statechange | ( | void * | datap | ) | [static] |
set a member's status based on device state of that member's interface
Definition at line 1506 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().
01507 { 01508 struct statechange *sc = datap; 01509 struct ao2_iterator miter, qiter; 01510 struct member *m; 01511 struct call_queue *q; 01512 char interface[80], *slash_pos; 01513 int found = 0; 01514 01515 qiter = ao2_iterator_init(queues, 0); 01516 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) { 01517 ao2_lock(q); 01518 01519 miter = ao2_iterator_init(q->members, 0); 01520 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01521 ast_copy_string(interface, m->state_interface, sizeof(interface)); 01522 01523 if ((slash_pos = strchr(interface, '/'))) 01524 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) 01525 *slash_pos = '\0'; 01526 01527 if (!strcasecmp(interface, sc->dev)) { 01528 found = 1; 01529 update_status(q, m, sc->state); 01530 ao2_ref(m, -1); 01531 break; 01532 } 01533 } 01534 ao2_iterator_destroy(&miter); 01535 01536 ao2_unlock(q); 01537 queue_t_unref(q, "Done with iterator"); 01538 } 01539 ao2_iterator_destroy(&qiter); 01540 01541 if (found) 01542 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state)); 01543 else 01544 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)); 01545 01546 ast_free(sc); 01547 return 0; 01548 }
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 2933 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().
02934 { 02935 struct callattempt *oo; 02936 02937 while (outgoing) { 02938 /* If someone else answered the call we should indicate this in the CANCEL */ 02939 /* Hangup any existing lines we have open */ 02940 if (outgoing->chan && (outgoing->chan != exception)) { 02941 if (exception || cancel_answered_elsewhere) 02942 ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE); 02943 ast_hangup(outgoing->chan); 02944 } 02945 oo = outgoing; 02946 outgoing = outgoing->q_next; 02947 ast_aoc_destroy_decoded(oo->aoc_s_rate_list); 02948 callattempt_free(oo); 02949 } 02950 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Initialize Queue default values.
Definition at line 1716 of file app_queue.c.
References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::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::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().
01717 { 01718 int i; 01719 struct penalty_rule *pr_iter; 01720 01721 q->dead = 0; 01722 q->retry = DEFAULT_RETRY; 01723 q->timeout = DEFAULT_TIMEOUT; 01724 q->maxlen = 0; 01725 q->announcefrequency = 0; 01726 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY; 01727 q->announceholdtime = 1; 01728 q->announcepositionlimit = 10; /* Default 10 positions */ 01729 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */ 01730 q->roundingseconds = 0; /* Default - don't announce seconds */ 01731 q->servicelevel = 0; 01732 q->ringinuse = 1; 01733 q->setinterfacevar = 0; 01734 q->setqueuevar = 0; 01735 q->setqueueentryvar = 0; 01736 q->autofill = autofill_default; 01737 q->montype = montype_default; 01738 q->monfmt[0] = '\0'; 01739 q->reportholdtime = 0; 01740 q->wrapuptime = 0; 01741 q->penaltymemberslimit = 0; 01742 q->joinempty = 0; 01743 q->leavewhenempty = 0; 01744 q->memberdelay = 0; 01745 q->maskmemberstatus = 0; 01746 q->eventwhencalled = 0; 01747 q->weight = 0; 01748 q->timeoutrestart = 0; 01749 q->periodicannouncefrequency = 0; 01750 q->randomperiodicannounce = 0; 01751 q->numperiodicannounce = 0; 01752 q->autopause = QUEUE_AUTOPAUSE_OFF; 01753 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01754 if (!q->members) { 01755 if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED) 01756 /* linear strategy depends on order, so we have to place all members in a single bucket */ 01757 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); 01758 else 01759 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 01760 } 01761 q->found = 1; 01762 01763 ast_string_field_set(q, sound_next, "queue-youarenext"); 01764 ast_string_field_set(q, sound_thereare, "queue-thereare"); 01765 ast_string_field_set(q, sound_calls, "queue-callswaiting"); 01766 ast_string_field_set(q, queue_quantity1, "queue-quantity1"); 01767 ast_string_field_set(q, queue_quantity2, "queue-quantity2"); 01768 ast_string_field_set(q, sound_holdtime, "queue-holdtime"); 01769 ast_string_field_set(q, sound_minutes, "queue-minutes"); 01770 ast_string_field_set(q, sound_minute, "queue-minute"); 01771 ast_string_field_set(q, sound_seconds, "queue-seconds"); 01772 ast_string_field_set(q, sound_thanks, "queue-thankyou"); 01773 ast_string_field_set(q, sound_reporthold, "queue-reporthold"); 01774 01775 if (!q->sound_periodicannounce[0]) { 01776 q->sound_periodicannounce[0] = ast_str_create(32); 01777 } 01778 01779 if (q->sound_periodicannounce[0]) { 01780 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce"); 01781 } 01782 01783 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 01784 if (q->sound_periodicannounce[i]) 01785 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", ""); 01786 } 01787 01788 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) 01789 ast_free(pr_iter); 01790 }
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 1370 of file app_queue.c.
References call_queue::head, and queue_ref().
Referenced by join_queue().
01371 { 01372 struct queue_ent *cur; 01373 01374 if (!q || !new) 01375 return; 01376 if (prev) { 01377 cur = prev->next; 01378 prev->next = new; 01379 } else { 01380 cur = q->head; 01381 q->head = new; 01382 } 01383 new->next = cur; 01384 01385 /* every queue_ent must have a reference to it's parent call_queue, this 01386 * reference does not go away until the end of the queue_ent's life, meaning 01387 * that even when the queue_ent leaves the call_queue this ref must remain. */ 01388 queue_ref(q); 01389 new->parent = q; 01390 new->pos = ++(*pos); 01391 new->opos = *pos; 01392 }
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 1821 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().
01822 { 01823 char *timestr, *maxstr, *minstr, *contentdup; 01824 struct penalty_rule *rule = NULL, *rule_iter; 01825 struct rule_list *rl_iter; 01826 int penaltychangetime, inserted = 0; 01827 01828 if (!(rule = ast_calloc(1, sizeof(*rule)))) { 01829 return -1; 01830 } 01831 01832 contentdup = ast_strdupa(content); 01833 01834 if (!(maxstr = strchr(contentdup, ','))) { 01835 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum); 01836 ast_free(rule); 01837 return -1; 01838 } 01839 01840 *maxstr++ = '\0'; 01841 timestr = contentdup; 01842 01843 if ((penaltychangetime = atoi(timestr)) < 0) { 01844 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum); 01845 ast_free(rule); 01846 return -1; 01847 } 01848 01849 rule->time = penaltychangetime; 01850 01851 if ((minstr = strchr(maxstr,','))) 01852 *minstr++ = '\0'; 01853 01854 /* The last check will evaluate true if either no penalty change is indicated for a given rule 01855 * OR if a min penalty change is indicated but no max penalty change is */ 01856 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') { 01857 rule->max_relative = 1; 01858 } 01859 01860 rule->max_value = atoi(maxstr); 01861 01862 if (!ast_strlen_zero(minstr)) { 01863 if (*minstr == '+' || *minstr == '-') 01864 rule->min_relative = 1; 01865 rule->min_value = atoi(minstr); 01866 } else /*there was no minimum specified, so assume this means no change*/ 01867 rule->min_relative = 1; 01868 01869 /*We have the rule made, now we need to insert it where it belongs*/ 01870 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){ 01871 if (strcasecmp(rl_iter->name, list_name)) 01872 continue; 01873 01874 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) { 01875 if (rule->time < rule_iter->time) { 01876 AST_LIST_INSERT_BEFORE_CURRENT(rule, list); 01877 inserted = 1; 01878 break; 01879 } 01880 } 01881 AST_LIST_TRAVERSE_SAFE_END; 01882 01883 if (!inserted) { 01884 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list); 01885 inserted = 1; 01886 } 01887 01888 break; 01889 } 01890 01891 if (!inserted) { 01892 ast_log(LOG_WARNING, "Unknown rule list name %s; ignoring.\n", list_name); 01893 ast_free(rule); 01894 return -1; 01895 } 01896 return 0; 01897 }
static const char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 1208 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().
01209 { 01210 int x; 01211 01212 for (x = 0; x < ARRAY_LEN(strategies); x++) { 01213 if (strategy == strategies[x].strategy) 01214 return strategies[x].name; 01215 } 01216 01217 return "<unknown>"; 01218 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static, read] |
Definition at line 5399 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().
05400 { 05401 struct member *mem; 05402 struct ao2_iterator mem_iter; 05403 05404 if (!q) 05405 return NULL; 05406 05407 mem_iter = ao2_iterator_init(q->members, 0); 05408 while ((mem = ao2_iterator_next(&mem_iter))) { 05409 if (!strcasecmp(interface, mem->interface)) { 05410 ao2_iterator_destroy(&mem_iter); 05411 return mem; 05412 } 05413 ao2_ref(mem, -1); 05414 } 05415 ao2_iterator_destroy(&mem_iter); 05416 05417 return NULL; 05418 }
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 4132 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().
04133 { 04134 struct queue_ent *ch; 04135 int res; 04136 int avl; 04137 int idx = 0; 04138 /* This needs a lock. How many members are available to be served? */ 04139 ao2_lock(qe->parent); 04140 04141 avl = num_available_members(qe->parent); 04142 04143 ch = qe->parent->head; 04144 04145 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); 04146 04147 while ((idx < avl) && (ch) && (ch != qe)) { 04148 if (!ch->pending) 04149 idx++; 04150 ch = ch->next; 04151 } 04152 04153 ao2_unlock(qe->parent); 04154 /* If the queue entry is within avl [the number of available members] calls from the top ... 04155 * Autofill and position check added to support autofill=no (as only calls 04156 * from the front of the queue are valid when autofill is disabled) 04157 */ 04158 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) { 04159 ast_debug(1, "It's our turn (%s).\n", qe->chan->name); 04160 res = 1; 04161 } else { 04162 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); 04163 res = 0; 04164 } 04165 04166 return res; 04167 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason, | |||
int | position | |||
) | [static] |
Definition at line 2543 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().
02544 { 02545 struct call_queue *q; 02546 struct queue_ent *cur, *prev = NULL; 02547 int res = -1; 02548 int pos = 0; 02549 int inserted = 0; 02550 02551 if (!(q = load_realtime_queue(queuename))) 02552 return res; 02553 02554 ao2_lock(q); 02555 02556 /* This is our one */ 02557 if (q->joinempty) { 02558 int status = 0; 02559 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) { 02560 *reason = QUEUE_JOINEMPTY; 02561 ao2_unlock(q); 02562 queue_t_unref(q, "Done with realtime queue"); 02563 return res; 02564 } 02565 } 02566 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) 02567 *reason = QUEUE_FULL; 02568 else if (*reason == QUEUE_UNKNOWN) { 02569 /* There's space for us, put us at the right position inside 02570 * the queue. 02571 * Take into account the priority of the calling user */ 02572 inserted = 0; 02573 prev = NULL; 02574 cur = q->head; 02575 while (cur) { 02576 /* We have higher priority than the current user, enter 02577 * before him, after all the other users with priority 02578 * higher or equal to our priority. */ 02579 if ((!inserted) && (qe->prio > cur->prio)) { 02580 insert_entry(q, prev, qe, &pos); 02581 inserted = 1; 02582 } 02583 /* <= is necessary for the position comparison because it may not be possible to enter 02584 * at our desired position since higher-priority callers may have taken the position we want 02585 */ 02586 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) { 02587 insert_entry(q, prev, qe, &pos); 02588 inserted = 1; 02589 /*pos is incremented inside insert_entry, so don't need to add 1 here*/ 02590 if (position < pos) { 02591 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos); 02592 } 02593 } 02594 cur->pos = ++pos; 02595 prev = cur; 02596 cur = cur->next; 02597 } 02598 /* No luck, join at the end of the queue */ 02599 if (!inserted) 02600 insert_entry(q, prev, qe, &pos); 02601 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 02602 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 02603 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 02604 q->count++; 02605 res = 0; 02606 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join", 02607 "Channel: %s\r\n" 02608 "CallerIDNum: %s\r\n" 02609 "CallerIDName: %s\r\n" 02610 "ConnectedLineNum: %s\r\n" 02611 "ConnectedLineName: %s\r\n" 02612 "Queue: %s\r\n" 02613 "Position: %d\r\n" 02614 "Count: %d\r\n" 02615 "Uniqueid: %s\r\n", 02616 qe->chan->name, 02617 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */ 02618 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 02619 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */ 02620 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"), 02621 q->name, qe->pos, q->count, qe->chan->uniqueid ); 02622 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 02623 } 02624 ao2_unlock(q); 02625 queue_t_unref(q, "Done with realtime queue"); 02626 02627 return res; 02628 }
static int kill_dead_members | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 6931 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 7075 of file app_queue.c.
References ast_strlen_zero(), CMP_MATCH, and call_queue::dead.
Referenced by reload_queues().
07076 { 07077 struct call_queue *q = obj; 07078 char *queuename = arg; 07079 if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) { 07080 return CMP_MATCH; 07081 } else { 07082 return 0; 07083 } 07084 }
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 2855 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().
02856 { 02857 struct call_queue *q; 02858 struct queue_ent *current, *prev = NULL; 02859 struct penalty_rule *pr_iter; 02860 int pos = 0; 02861 02862 if (!(q = qe->parent)) 02863 return; 02864 queue_t_ref(q, "Copy queue pointer from queue entry"); 02865 ao2_lock(q); 02866 02867 prev = NULL; 02868 for (current = q->head; current; current = current->next) { 02869 if (current == qe) { 02870 char posstr[20]; 02871 q->count--; 02872 02873 /* Take us out of the queue */ 02874 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave", 02875 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n", 02876 qe->chan->name, q->name, q->count, qe->pos, qe->chan->uniqueid); 02877 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 02878 /* Take us out of the queue */ 02879 if (prev) 02880 prev->next = current->next; 02881 else 02882 q->head = current->next; 02883 /* Free penalty rules */ 02884 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) 02885 ast_free(pr_iter); 02886 snprintf(posstr, sizeof(posstr), "%d", qe->pos); 02887 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr); 02888 } else { 02889 /* Renumber the people after us in the queue based on a new count */ 02890 current->pos = ++pos; 02891 prev = current; 02892 } 02893 } 02894 ao2_unlock(q); 02895 02896 /*If the queue is a realtime queue, check to see if it's still defined in real time*/ 02897 if (q->realtime) { 02898 struct ast_variable *var; 02899 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) { 02900 q->dead = 1; 02901 } else { 02902 ast_variables_destroy(var); 02903 } 02904 } 02905 02906 if (q->dead) { 02907 /* It's dead and nobody is in it, so kill it */ 02908 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container"); 02909 } 02910 /* unref the explicit ref earlier in the function */ 02911 queue_t_unref(q, "Expire copied reference"); 02912 }
static int load_module | ( | void | ) | [static] |
Definition at line 8748 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().
08749 { 08750 int res; 08751 struct ast_context *con; 08752 struct ast_flags mask = {AST_FLAGS_ALL, }; 08753 08754 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb); 08755 08756 use_weight = 0; 08757 08758 if (reload_handler(0, &mask, NULL)) 08759 return AST_MODULE_LOAD_DECLINE; 08760 08761 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue"); 08762 if (!con) 08763 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n"); 08764 else 08765 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue"); 08766 08767 if (queue_persistent_members) 08768 reload_queue_members(); 08769 08770 ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers)); 08771 08772 ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue)); 08773 res = ast_register_application_xml(app, queue_exec); 08774 res |= ast_register_application_xml(app_aqm, aqm_exec); 08775 res |= ast_register_application_xml(app_rqm, rqm_exec); 08776 res |= ast_register_application_xml(app_pqm, pqm_exec); 08777 res |= ast_register_application_xml(app_upqm, upqm_exec); 08778 res |= ast_register_application_xml(app_ql, ql_exec); 08779 res |= ast_manager_register_xml("Queues", 0, manager_queues_show); 08780 res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status); 08781 res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary); 08782 res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member); 08783 res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member); 08784 res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member); 08785 res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom); 08786 res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty); 08787 res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show); 08788 res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload); 08789 res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset); 08790 res |= ast_custom_function_register(&queuevar_function); 08791 res |= ast_custom_function_register(&queueexists_function); 08792 res |= ast_custom_function_register(&queuemembercount_function); 08793 res |= ast_custom_function_register(&queuemembercount_dep); 08794 res |= ast_custom_function_register(&queuememberlist_function); 08795 res |= ast_custom_function_register(&queuewaitingcount_function); 08796 res |= ast_custom_function_register(&queuememberpenalty_function); 08797 08798 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) { 08799 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); 08800 } 08801 08802 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */ 08803 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) { 08804 res = -1; 08805 } 08806 08807 ast_extension_state_add(NULL, NULL, extension_state_cb, NULL); 08808 08809 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL); 08810 08811 return res ? AST_MODULE_LOAD_DECLINE : 0; 08812 }
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 2416 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().
02417 { 02418 struct ast_variable *queue_vars; 02419 struct ast_config *member_config = NULL; 02420 struct call_queue *q = NULL, tmpq = { 02421 .name = queuename, 02422 }; 02423 int prev_weight = 0; 02424 02425 /* Find the queue in the in-core list first. */ 02426 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first"); 02427 02428 if (!q || q->realtime) { 02429 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all 02430 queue operations while waiting for the DB. 02431 02432 This will be two separate database transactions, so we might 02433 see queue parameters as they were before another process 02434 changed the queue and member list as it was after the change. 02435 Thus we might see an empty member list when a queue is 02436 deleted. In practise, this is unlikely to cause a problem. */ 02437 02438 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL); 02439 if (queue_vars) { 02440 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL); 02441 if (!member_config) { 02442 ast_debug(1, "No queue_members defined in config extconfig.conf\n"); 02443 member_config = ast_config_new(); 02444 } 02445 } 02446 if (q) { 02447 prev_weight = q->weight ? 1 : 0; 02448 queue_t_unref(q, "Need to find realtime queue"); 02449 } 02450 02451 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 02452 ast_config_destroy(member_config); 02453 ast_variables_destroy(queue_vars); 02454 02455 /* update the use_weight value if the queue's has gained or lost a weight */ 02456 if (q) { 02457 if (!q->weight && prev_weight) { 02458 ast_atomic_fetchadd_int(&use_weight, -1); 02459 } 02460 if (q->weight && !prev_weight) { 02461 ast_atomic_fetchadd_int(&use_weight, +1); 02462 } 02463 } 02464 /* Other cases will end up with the proper value for use_weight */ 02465 } else { 02466 update_realtime_members(q); 02467 } 02468 return q; 02469 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7712 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().
07713 { 07714 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface; 07715 int paused, penalty = 0; 07716 07717 queuename = astman_get_header(m, "Queue"); 07718 interface = astman_get_header(m, "Interface"); 07719 penalty_s = astman_get_header(m, "Penalty"); 07720 paused_s = astman_get_header(m, "Paused"); 07721 membername = astman_get_header(m, "MemberName"); 07722 state_interface = astman_get_header(m, "StateInterface"); 07723 07724 if (ast_strlen_zero(queuename)) { 07725 astman_send_error(s, m, "'Queue' not specified."); 07726 return 0; 07727 } 07728 07729 if (ast_strlen_zero(interface)) { 07730 astman_send_error(s, m, "'Interface' not specified."); 07731 return 0; 07732 } 07733 07734 if (ast_strlen_zero(penalty_s)) 07735 penalty = 0; 07736 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) 07737 penalty = 0; 07738 07739 if (ast_strlen_zero(paused_s)) 07740 paused = 0; 07741 else 07742 paused = abs(ast_true(paused_s)); 07743 07744 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) { 07745 case RES_OKAY: 07746 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", paused ? "PAUSED" : ""); 07747 astman_send_ack(s, m, "Added interface to queue"); 07748 break; 07749 case RES_EXISTS: 07750 astman_send_error(s, m, "Unable to add interface: Already there"); 07751 break; 07752 case RES_NOSUCHQUEUE: 07753 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 07754 break; 07755 case RES_OUTOFMEMORY: 07756 astman_send_error(s, m, "Out of memory"); 07757 break; 07758 } 07759 07760 return 0; 07761 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7797 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().
07798 { 07799 const char *queuename, *interface, *paused_s, *reason; 07800 int paused; 07801 07802 interface = astman_get_header(m, "Interface"); 07803 paused_s = astman_get_header(m, "Paused"); 07804 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 07805 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */ 07806 07807 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 07808 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 07809 return 0; 07810 } 07811 07812 paused = abs(ast_true(paused_s)); 07813 07814 if (set_member_paused(queuename, interface, reason, paused)) 07815 astman_send_error(s, m, "Interface not found"); 07816 else 07817 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 07818 return 0; 07819 }
static int manager_queue_log_custom | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7821 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().
07822 { 07823 const char *queuename, *event, *message, *interface, *uniqueid; 07824 07825 queuename = astman_get_header(m, "Queue"); 07826 uniqueid = astman_get_header(m, "UniqueId"); 07827 interface = astman_get_header(m, "Interface"); 07828 event = astman_get_header(m, "Event"); 07829 message = astman_get_header(m, "Message"); 07830 07831 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) { 07832 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters."); 07833 return 0; 07834 } 07835 07836 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message); 07837 astman_send_ack(s, m, "Event added successfully"); 07838 07839 return 0; 07840 }
static int manager_queue_member_penalty | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7920 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().
07921 { 07922 const char *queuename, *interface, *penalty_s; 07923 int penalty; 07924 07925 interface = astman_get_header(m, "Interface"); 07926 penalty_s = astman_get_header(m, "Penalty"); 07927 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ 07928 queuename = astman_get_header(m, "Queue"); 07929 07930 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { 07931 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); 07932 return 0; 07933 } 07934 07935 penalty = atoi(penalty_s); 07936 07937 if (set_member_penalty((char *)queuename, (char *)interface, penalty)) 07938 astman_send_error(s, m, "Invalid interface, queuename or penalty"); 07939 else 07940 astman_send_ack(s, m, "Interface penalty set successfully"); 07941 07942 return 0; 07943 }
static int manager_queue_reload | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7842 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().
07843 { 07844 struct ast_flags mask = {0,}; 07845 const char *queuename = NULL; 07846 int header_found = 0; 07847 07848 queuename = astman_get_header(m, "Queue"); 07849 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) { 07850 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 07851 header_found = 1; 07852 } 07853 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) { 07854 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 07855 header_found = 1; 07856 } 07857 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) { 07858 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 07859 header_found = 1; 07860 } 07861 07862 if (!header_found) { 07863 ast_set_flag(&mask, AST_FLAGS_ALL); 07864 } 07865 07866 if (!reload_handler(1, &mask, queuename)) { 07867 astman_send_ack(s, m, "Queue reloaded successfully"); 07868 } else { 07869 astman_send_error(s, m, "Error encountered while reloading queue"); 07870 } 07871 return 0; 07872 }
static int manager_queue_reset | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7874 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().
07875 { 07876 const char *queuename = NULL; 07877 struct ast_flags mask = {QUEUE_RESET_STATS,}; 07878 07879 queuename = astman_get_header(m, "Queue"); 07880 07881 if (!reload_handler(1, &mask, queuename)) { 07882 astman_send_ack(s, m, "Queue stats reset successfully"); 07883 } else { 07884 astman_send_error(s, m, "Error encountered while resetting queue stats"); 07885 } 07886 return 0; 07887 }
static int manager_queue_rule_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7501 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().
07502 { 07503 const char *rule = astman_get_header(m, "Rule"); 07504 const char *id = astman_get_header(m, "ActionID"); 07505 struct rule_list *rl_iter; 07506 struct penalty_rule *pr_iter; 07507 07508 astman_append(s, "Response: Success\r\n"); 07509 if (!ast_strlen_zero(id)) { 07510 astman_append(s, "ActionID: %s\r\n", id); 07511 } 07512 07513 AST_LIST_LOCK(&rule_lists); 07514 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 07515 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) { 07516 astman_append(s, "RuleList: %s\r\n", rl_iter->name); 07517 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 07518 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 ); 07519 } 07520 if (!ast_strlen_zero(rule)) 07521 break; 07522 } 07523 } 07524 AST_LIST_UNLOCK(&rule_lists); 07525 07526 /* 07527 * Two blank lines instead of one because the Response and 07528 * ActionID headers used to not be present. 07529 */ 07530 astman_append(s, "\r\n\r\n"); 07531 07532 return RESULT_SUCCESS; 07533 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7491 of file app_queue.c.
References __queues_show(), astman_append(), and RESULT_SUCCESS.
Referenced by load_module().
07492 { 07493 static const char * const a[] = { "queue", "show" }; 07494 07495 __queues_show(s, -1, 2, a); 07496 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 07497 07498 return RESULT_SUCCESS; 07499 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Queue status info via AMI.
Definition at line 7611 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, 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().
07612 { 07613 time_t now; 07614 int pos; 07615 const char *id = astman_get_header(m,"ActionID"); 07616 const char *queuefilter = astman_get_header(m,"Queue"); 07617 const char *memberfilter = astman_get_header(m,"Member"); 07618 char idText[256] = ""; 07619 struct call_queue *q; 07620 struct queue_ent *qe; 07621 float sl = 0; 07622 struct member *mem; 07623 struct ao2_iterator queue_iter; 07624 struct ao2_iterator mem_iter; 07625 07626 astman_send_ack(s, m, "Queue status will follow"); 07627 time(&now); 07628 if (!ast_strlen_zero(id)) 07629 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 07630 07631 queue_iter = ao2_iterator_init(queues, 0); 07632 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07633 ao2_lock(q); 07634 07635 /* List queue properties */ 07636 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 07637 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 07638 astman_append(s, "Event: QueueParams\r\n" 07639 "Queue: %s\r\n" 07640 "Max: %d\r\n" 07641 "Strategy: %s\r\n" 07642 "Calls: %d\r\n" 07643 "Holdtime: %d\r\n" 07644 "TalkTime: %d\r\n" 07645 "Completed: %d\r\n" 07646 "Abandoned: %d\r\n" 07647 "ServiceLevel: %d\r\n" 07648 "ServicelevelPerf: %2.1f\r\n" 07649 "Weight: %d\r\n" 07650 "%s" 07651 "\r\n", 07652 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, 07653 q->callsabandoned, q->servicelevel, sl, q->weight, idText); 07654 /* List Queue Members */ 07655 mem_iter = ao2_iterator_init(q->members, 0); 07656 while ((mem = ao2_iterator_next(&mem_iter))) { 07657 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) { 07658 astman_append(s, "Event: QueueMember\r\n" 07659 "Queue: %s\r\n" 07660 "Name: %s\r\n" 07661 "Location: %s\r\n" 07662 "Membership: %s\r\n" 07663 "Penalty: %d\r\n" 07664 "CallsTaken: %d\r\n" 07665 "LastCall: %d\r\n" 07666 "Status: %d\r\n" 07667 "Paused: %d\r\n" 07668 "%s" 07669 "\r\n", 07670 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 07671 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 07672 } 07673 ao2_ref(mem, -1); 07674 } 07675 ao2_iterator_destroy(&mem_iter); 07676 /* List Queue Entries */ 07677 pos = 1; 07678 for (qe = q->head; qe; qe = qe->next) { 07679 astman_append(s, "Event: QueueEntry\r\n" 07680 "Queue: %s\r\n" 07681 "Position: %d\r\n" 07682 "Channel: %s\r\n" 07683 "Uniqueid: %s\r\n" 07684 "CallerIDNum: %s\r\n" 07685 "CallerIDName: %s\r\n" 07686 "ConnectedLineNum: %s\r\n" 07687 "ConnectedLineName: %s\r\n" 07688 "Wait: %ld\r\n" 07689 "%s" 07690 "\r\n", 07691 q->name, pos++, qe->chan->name, qe->chan->uniqueid, 07692 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"), 07693 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 07694 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"), 07695 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"), 07696 (long) (now - qe->start), idText); 07697 } 07698 } 07699 ao2_unlock(q); 07700 queue_t_unref(q, "Done with iterator"); 07701 } 07702 ao2_iterator_destroy(&queue_iter); 07703 07704 astman_append(s, 07705 "Event: QueueStatusComplete\r\n" 07706 "%s" 07707 "\r\n",idText); 07708 07709 return RESULT_SUCCESS; 07710 }
static int manager_queues_summary | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Summary of queue info via the AMI.
Definition at line 7536 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().
07537 { 07538 time_t now; 07539 int qmemcount = 0; 07540 int qmemavail = 0; 07541 int qchancount = 0; 07542 int qlongestholdtime = 0; 07543 const char *id = astman_get_header(m, "ActionID"); 07544 const char *queuefilter = astman_get_header(m, "Queue"); 07545 char idText[256] = ""; 07546 struct call_queue *q; 07547 struct queue_ent *qe; 07548 struct member *mem; 07549 struct ao2_iterator queue_iter; 07550 struct ao2_iterator mem_iter; 07551 07552 astman_send_ack(s, m, "Queue summary will follow"); 07553 time(&now); 07554 if (!ast_strlen_zero(id)) 07555 snprintf(idText, 256, "ActionID: %s\r\n", id); 07556 queue_iter = ao2_iterator_init(queues, 0); 07557 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07558 ao2_lock(q); 07559 07560 /* List queue properties */ 07561 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 07562 /* Reset the necessary local variables if no queuefilter is set*/ 07563 qmemcount = 0; 07564 qmemavail = 0; 07565 qchancount = 0; 07566 qlongestholdtime = 0; 07567 07568 /* List Queue Members */ 07569 mem_iter = ao2_iterator_init(q->members, 0); 07570 while ((mem = ao2_iterator_next(&mem_iter))) { 07571 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) { 07572 ++qmemcount; 07573 if (member_status_available(mem->status) && !mem->paused) { 07574 ++qmemavail; 07575 } 07576 } 07577 ao2_ref(mem, -1); 07578 } 07579 ao2_iterator_destroy(&mem_iter); 07580 for (qe = q->head; qe; qe = qe->next) { 07581 if ((now - qe->start) > qlongestholdtime) { 07582 qlongestholdtime = now - qe->start; 07583 } 07584 ++qchancount; 07585 } 07586 astman_append(s, "Event: QueueSummary\r\n" 07587 "Queue: %s\r\n" 07588 "LoggedIn: %d\r\n" 07589 "Available: %d\r\n" 07590 "Callers: %d\r\n" 07591 "HoldTime: %d\r\n" 07592 "TalkTime: %d\r\n" 07593 "LongestHoldTime: %d\r\n" 07594 "%s" 07595 "\r\n", 07596 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText); 07597 } 07598 ao2_unlock(q); 07599 queue_t_unref(q, "Done with iterator"); 07600 } 07601 ao2_iterator_destroy(&queue_iter); 07602 astman_append(s, 07603 "Event: QueueSummaryComplete\r\n" 07604 "%s" 07605 "\r\n", idText); 07606 07607 return RESULT_SUCCESS; 07608 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7763 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().
07764 { 07765 const char *queuename, *interface; 07766 07767 queuename = astman_get_header(m, "Queue"); 07768 interface = astman_get_header(m, "Interface"); 07769 07770 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 07771 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 07772 return 0; 07773 } 07774 07775 switch (remove_from_queue(queuename, interface)) { 07776 case RES_OKAY: 07777 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 07778 astman_send_ack(s, m, "Removed interface from queue"); 07779 break; 07780 case RES_EXISTS: 07781 astman_send_error(s, m, "Unable to remove interface: Not there"); 07782 break; 07783 case RES_NOSUCHQUEUE: 07784 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 07785 break; 07786 case RES_OUTOFMEMORY: 07787 astman_send_error(s, m, "Out of memory"); 07788 break; 07789 case RES_NOT_DYNAMIC: 07790 astman_send_error(s, m, "Member not dynamic"); 07791 break; 07792 } 07793 07794 return 0; 07795 }
static int mark_dead_and_unfound | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7064 of file app_queue.c.
References ast_strlen_zero(), call_queue::dead, call_queue::found, and call_queue::realtime.
Referenced by reload_queues().
07065 { 07066 struct call_queue *q = obj; 07067 char *queuename = arg; 07068 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) { 07069 q->dead = 1; 07070 q->found = 0; 07071 } 07072 return 0; 07073 }
static int mark_member_dead | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 6922 of file app_queue.c.
References member::delme, and member::dynamic.
Referenced by reload_single_queue().
static void member_add_to_queue | ( | struct call_queue * | queue, | |
struct member * | mem | |||
) | [static] |
Definition at line 2143 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().
02144 { 02145 ao2_lock(queue->members); 02146 mem->queuepos = ao2_container_count(queue->members); 02147 ao2_link(queue->members, mem); 02148 ao2_unlock(queue->members); 02149 }
static void member_call_pending_clear | ( | struct member * | mem | ) | [static] |
Definition at line 3104 of file app_queue.c.
References ao2_lock, ao2_unlock, and member::call_pending.
Referenced by can_ring_entry(), and ring_entry().
03105 { 03106 ao2_lock(mem); 03107 mem->call_pending = 0; 03108 ao2_unlock(mem); 03109 }
static int member_call_pending_set | ( | struct member * | mem | ) | [static] |
Definition at line 3119 of file app_queue.c.
References ao2_lock, ao2_unlock, and member::call_pending.
Referenced by can_ring_entry().
03120 { 03121 int old_pending; 03122 03123 ao2_lock(mem); 03124 old_pending = mem->call_pending; 03125 mem->call_pending = 1; 03126 ao2_unlock(mem); 03127 03128 return old_pending; 03129 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 1706 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 1694 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
01695 { 01696 const struct member *mem = obj; 01697 const char *chname = strchr(mem->interface, '/'); 01698 int ret = 0, i; 01699 if (!chname) 01700 chname = mem->interface; 01701 for (i = 0; i < 5 && chname[i]; i++) 01702 ret += compress_char(chname[i]) << (i * 6); 01703 return ret; 01704 }
static void member_remove_from_queue | ( | struct call_queue * | queue, | |
struct member * | mem | |||
) | [static] |
Definition at line 2157 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().
02158 { 02159 ao2_lock(queue->members); 02160 queue_member_follower_removal(queue, mem); 02161 ao2_unlink(queue->members, mem); 02162 ao2_unlock(queue->members); 02163 }
static int member_status_available | ( | int | status | ) | [static] |
Definition at line 3091 of file app_queue.c.
References AST_DEVICE_NOT_INUSE, and AST_DEVICE_UNKNOWN.
Referenced by can_ring_entry(), and manager_queues_summary().
03092 { 03093 return status == AST_DEVICE_NOT_INUSE || status == AST_DEVICE_UNKNOWN; 03094 }
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 2960 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, call_queue::autofill, call_queue::members, member::paused, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.
Referenced by compare_weight(), and is_our_turn().
02961 { 02962 struct member *mem; 02963 int avl = 0; 02964 struct ao2_iterator mem_iter; 02965 02966 mem_iter = ao2_iterator_init(q->members, 0); 02967 while ((mem = ao2_iterator_next(&mem_iter))) { 02968 switch (mem->status) { 02969 case AST_DEVICE_INUSE: 02970 if (!q->ringinuse) 02971 break; 02972 /* else fall through */ 02973 case AST_DEVICE_NOT_INUSE: 02974 case AST_DEVICE_UNKNOWN: 02975 if (!mem->paused) { 02976 avl++; 02977 } 02978 break; 02979 } 02980 ao2_ref(mem, -1); 02981 02982 /* If autofill is not enabled or if the queue's strategy is ringall, then 02983 * we really don't care about the number of available members so much as we 02984 * do that there is at least one available. 02985 * 02986 * In fact, we purposely will return from this function stating that only 02987 * one member is available if either of those conditions hold. That way, 02988 * functions which determine what action to take based on the number of available 02989 * members will operate properly. The reasoning is that even if multiple 02990 * members are available, only the head caller can actually be serviced. 02991 */ 02992 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { 02993 break; 02994 } 02995 } 02996 ao2_iterator_destroy(&mem_iter); 02997 02998 return avl; 02999 }
static void parse_empty_options | ( | const char * | value, | |
enum empty_conditions * | empty, | |||
int | joinempty | |||
) | [static] |
Definition at line 1899 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().
01900 { 01901 char *value_copy = ast_strdupa(value); 01902 char *option = NULL; 01903 while ((option = strsep(&value_copy, ","))) { 01904 if (!strcasecmp(option, "paused")) { 01905 *empty |= QUEUE_EMPTY_PAUSED; 01906 } else if (!strcasecmp(option, "penalty")) { 01907 *empty |= QUEUE_EMPTY_PENALTY; 01908 } else if (!strcasecmp(option, "inuse")) { 01909 *empty |= QUEUE_EMPTY_INUSE; 01910 } else if (!strcasecmp(option, "ringing")) { 01911 *empty |= QUEUE_EMPTY_RINGING; 01912 } else if (!strcasecmp(option, "invalid")) { 01913 *empty |= QUEUE_EMPTY_INVALID; 01914 } else if (!strcasecmp(option, "wrapup")) { 01915 *empty |= QUEUE_EMPTY_WRAPUP; 01916 } else if (!strcasecmp(option, "unavailable")) { 01917 *empty |= QUEUE_EMPTY_UNAVAILABLE; 01918 } else if (!strcasecmp(option, "unknown")) { 01919 *empty |= QUEUE_EMPTY_UNKNOWN; 01920 } else if (!strcasecmp(option, "loose")) { 01921 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID); 01922 } else if (!strcasecmp(option, "strict")) { 01923 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE); 01924 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) { 01925 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED); 01926 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) { 01927 *empty = 0; 01928 } else { 01929 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty"); 01930 } 01931 } 01932 }
static int play_file | ( | struct ast_channel * | chan, | |
const char * | filename | |||
) | [static] |
Definition at line 2630 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().
02631 { 02632 int res; 02633 02634 if (ast_strlen_zero(filename)) { 02635 return 0; 02636 } 02637 02638 if (!ast_fileexists(filename, NULL, chan->language)) { 02639 return 0; 02640 } 02641 02642 ast_stopstream(chan); 02643 02644 res = ast_streamfile(chan, filename, chan->language); 02645 if (!res) 02646 res = ast_waitstream(chan, AST_DIGIT_ANY); 02647 02648 ast_stopstream(chan); 02649 02650 return res; 02651 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
PauseQueueMember application.
Definition at line 5829 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().
05830 { 05831 char *parse; 05832 AST_DECLARE_APP_ARGS(args, 05833 AST_APP_ARG(queuename); 05834 AST_APP_ARG(interface); 05835 AST_APP_ARG(options); 05836 AST_APP_ARG(reason); 05837 ); 05838 05839 if (ast_strlen_zero(data)) { 05840 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n"); 05841 return -1; 05842 } 05843 05844 parse = ast_strdupa(data); 05845 05846 AST_STANDARD_APP_ARGS(args, parse); 05847 05848 if (ast_strlen_zero(args.interface)) { 05849 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 05850 return -1; 05851 } 05852 05853 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) { 05854 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 05855 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 05856 return 0; 05857 } 05858 05859 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 05860 05861 return 0; 05862 }
static int ql_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
QueueLog application.
Definition at line 6020 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().
06021 { 06022 char *parse; 06023 06024 AST_DECLARE_APP_ARGS(args, 06025 AST_APP_ARG(queuename); 06026 AST_APP_ARG(uniqueid); 06027 AST_APP_ARG(membername); 06028 AST_APP_ARG(event); 06029 AST_APP_ARG(params); 06030 ); 06031 06032 if (ast_strlen_zero(data)) { 06033 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n"); 06034 return -1; 06035 } 06036 06037 parse = ast_strdupa(data); 06038 06039 AST_STANDARD_APP_ARGS(args, parse); 06040 06041 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 06042 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 06043 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n"); 06044 return -1; 06045 } 06046 06047 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 06048 "%s", args.params ? args.params : ""); 06049 06050 return 0; 06051 }
static int queue_cmp_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1259 of file app_queue.c.
References CMP_MATCH, and CMP_STOP.
Referenced by load_module().
01260 { 01261 struct call_queue *q = obj, *q2 = arg; 01262 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0; 01263 }
static int queue_delme_members_decrement_followers | ( | void * | obj, | |
void * | arg, | |||
int | flag | |||
) | [static] |
Definition at line 1288 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().
01289 { 01290 struct member *mem = obj; 01291 struct call_queue *queue = arg; 01292 int rrpos = mem->queuepos; 01293 01294 if (mem->delme) { 01295 ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &rrpos); 01296 } 01297 01298 return 0; 01299 }
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 6094 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, 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().
06095 { 06096 int res=-1; 06097 int ringing=0; 06098 const char *user_priority; 06099 const char *max_penalty_str; 06100 const char *min_penalty_str; 06101 int prio; 06102 int qcontinue = 0; 06103 int max_penalty, min_penalty; 06104 enum queue_result reason = QUEUE_UNKNOWN; 06105 /* whether to exit Queue application after the timeout hits */ 06106 int tries = 0; 06107 int noption = 0; 06108 char *parse; 06109 int makeannouncement = 0; 06110 int position = 0; 06111 AST_DECLARE_APP_ARGS(args, 06112 AST_APP_ARG(queuename); 06113 AST_APP_ARG(options); 06114 AST_APP_ARG(url); 06115 AST_APP_ARG(announceoverride); 06116 AST_APP_ARG(queuetimeoutstr); 06117 AST_APP_ARG(agi); 06118 AST_APP_ARG(macro); 06119 AST_APP_ARG(gosub); 06120 AST_APP_ARG(rule); 06121 AST_APP_ARG(position); 06122 ); 06123 /* Our queue entry */ 06124 struct queue_ent qe = { 0 }; 06125 06126 if (ast_strlen_zero(data)) { 06127 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n"); 06128 return -1; 06129 } 06130 06131 parse = ast_strdupa(data); 06132 AST_STANDARD_APP_ARGS(args, parse); 06133 06134 /* Setup our queue entry */ 06135 qe.start = time(NULL); 06136 06137 /* set the expire time based on the supplied timeout; */ 06138 if (!ast_strlen_zero(args.queuetimeoutstr)) 06139 qe.expire = qe.start + atoi(args.queuetimeoutstr); 06140 else 06141 qe.expire = 0; 06142 06143 /* Get the priority from the variable ${QUEUE_PRIO} */ 06144 ast_channel_lock(chan); 06145 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 06146 if (user_priority) { 06147 if (sscanf(user_priority, "%30d", &prio) == 1) { 06148 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio); 06149 } else { 06150 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 06151 user_priority, chan->name); 06152 prio = 0; 06153 } 06154 } else { 06155 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n"); 06156 prio = 0; 06157 } 06158 06159 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 06160 06161 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 06162 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) { 06163 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty); 06164 } else { 06165 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 06166 max_penalty_str, chan->name); 06167 max_penalty = 0; 06168 } 06169 } else { 06170 max_penalty = 0; 06171 } 06172 06173 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) { 06174 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) { 06175 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty); 06176 } else { 06177 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n", 06178 min_penalty_str, chan->name); 06179 min_penalty = 0; 06180 } 06181 } else { 06182 min_penalty = 0; 06183 } 06184 ast_channel_unlock(chan); 06185 06186 if (args.options && (strchr(args.options, 'r'))) 06187 ringing = 1; 06188 06189 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) { 06190 qe.ring_when_ringing = 1; 06191 } 06192 06193 if (args.options && (strchr(args.options, 'c'))) 06194 qcontinue = 1; 06195 06196 if (args.position) { 06197 position = atoi(args.position); 06198 if (position < 0) { 06199 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename); 06200 position = 0; 06201 } 06202 } 06203 06204 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 06205 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 06206 06207 qe.chan = chan; 06208 qe.prio = prio; 06209 qe.max_penalty = max_penalty; 06210 qe.min_penalty = min_penalty; 06211 qe.last_pos_said = 0; 06212 qe.last_pos = 0; 06213 qe.last_periodic_announce_time = time(NULL); 06214 qe.last_periodic_announce_sound = 0; 06215 qe.valid_digits = 0; 06216 if (join_queue(args.queuename, &qe, &reason, position)) { 06217 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 06218 set_queue_result(chan, reason); 06219 return 0; 06220 } 06221 ast_assert(qe.parent != NULL); 06222 06223 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d", 06224 S_OR(args.url, ""), 06225 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""), 06226 qe.opos); 06227 copy_rules(&qe, args.rule); 06228 qe.pr = AST_LIST_FIRST(&qe.qe_rules); 06229 check_turns: 06230 if (ringing) { 06231 ast_indicate(chan, AST_CONTROL_RINGING); 06232 } else { 06233 ast_moh_start(chan, qe.moh, NULL); 06234 } 06235 06236 /* This is the wait loop for callers 2 through maxlen */ 06237 res = wait_our_turn(&qe, ringing, &reason); 06238 if (res) { 06239 goto stop; 06240 } 06241 06242 makeannouncement = 0; 06243 06244 for (;;) { 06245 /* This is the wait loop for the head caller*/ 06246 /* To exit, they may get their call answered; */ 06247 /* they may dial a digit from the queue context; */ 06248 /* or, they may timeout. */ 06249 06250 /* Leave if we have exceeded our queuetimeout */ 06251 if (qe.expire && (time(NULL) >= qe.expire)) { 06252 record_abandoned(&qe); 06253 reason = QUEUE_TIMEOUT; 06254 res = 0; 06255 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 06256 qe.pos, qe.opos, (long) time(NULL) - qe.start); 06257 break; 06258 } 06259 06260 if (makeannouncement) { 06261 /* Make a position announcement, if enabled */ 06262 if (qe.parent->announcefrequency) 06263 if ((res = say_position(&qe,ringing))) 06264 goto stop; 06265 } 06266 makeannouncement = 1; 06267 06268 /* Make a periodic announcement, if enabled */ 06269 if (qe.parent->periodicannouncefrequency) 06270 if ((res = say_periodic_announcement(&qe,ringing))) 06271 goto stop; 06272 06273 /* Leave if we have exceeded our queuetimeout */ 06274 if (qe.expire && (time(NULL) >= qe.expire)) { 06275 record_abandoned(&qe); 06276 reason = QUEUE_TIMEOUT; 06277 res = 0; 06278 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 06279 break; 06280 } 06281 06282 /* see if we need to move to the next penalty level for this queue */ 06283 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) { 06284 update_qe_rule(&qe); 06285 } 06286 06287 /* Try calling all queue members for 'timeout' seconds */ 06288 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing); 06289 if (res) { 06290 goto stop; 06291 } 06292 06293 if (qe.parent->leavewhenempty) { 06294 int status = 0; 06295 if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) { 06296 record_abandoned(&qe); 06297 reason = QUEUE_LEAVEEMPTY; 06298 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 06299 res = 0; 06300 break; 06301 } 06302 } 06303 06304 /* exit after 'timeout' cycle if 'n' option enabled */ 06305 if (noption && tries >= ao2_container_count(qe.parent->members)) { 06306 ast_verb(3, "Exiting on time-out cycle\n"); 06307 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 06308 record_abandoned(&qe); 06309 reason = QUEUE_TIMEOUT; 06310 res = 0; 06311 break; 06312 } 06313 06314 06315 /* Leave if we have exceeded our queuetimeout */ 06316 if (qe.expire && (time(NULL) >= qe.expire)) { 06317 record_abandoned(&qe); 06318 reason = QUEUE_TIMEOUT; 06319 res = 0; 06320 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start); 06321 break; 06322 } 06323 06324 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 06325 update_realtime_members(qe.parent); 06326 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 06327 res = wait_a_bit(&qe); 06328 if (res) 06329 goto stop; 06330 06331 /* Since this is a priority queue and 06332 * it is not sure that we are still at the head 06333 * of the queue, go and check for our turn again. 06334 */ 06335 if (!is_our_turn(&qe)) { 06336 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name); 06337 goto check_turns; 06338 } 06339 } 06340 06341 stop: 06342 if (res) { 06343 if (res < 0) { 06344 if (!qe.handled) { 06345 record_abandoned(&qe); 06346 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 06347 "%d|%d|%ld", qe.pos, qe.opos, 06348 (long) time(NULL) - qe.start); 06349 res = -1; 06350 } else if (qcontinue) { 06351 reason = QUEUE_CONTINUE; 06352 res = 0; 06353 } 06354 } else if (qe.valid_digits) { 06355 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 06356 "%s|%d", qe.digits, qe.pos); 06357 } 06358 } 06359 06360 /* Don't allow return code > 0 */ 06361 if (res >= 0) { 06362 res = 0; 06363 if (ringing) { 06364 ast_indicate(chan, -1); 06365 } else { 06366 ast_moh_stop(chan); 06367 } 06368 ast_stopstream(chan); 06369 } 06370 06371 set_queue_variables(qe.parent, qe.chan); 06372 06373 leave_queue(&qe); 06374 if (reason != QUEUE_UNKNOWN) 06375 set_queue_result(chan, reason); 06376 06377 /* 06378 * every queue_ent is given a reference to it's parent 06379 * call_queue when it joins the queue. This ref must be taken 06380 * away right before the queue_ent is destroyed. In this case 06381 * the queue_ent is about to be returned on the stack 06382 */ 06383 qe.parent = queue_unref(qe.parent); 06384 06385 return res; 06386 }
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 6440 of file app_queue.c.
References ast_log(), ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, and queue_t_unref.
06441 { 06442 struct call_queue *q; 06443 06444 buf[0] = '\0'; 06445 06446 if (ast_strlen_zero(data)) { 06447 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06448 return -1; 06449 } 06450 q = load_realtime_queue(data); 06451 snprintf(buf, len, "%d", q != NULL? 1 : 0); 06452 if (q) { 06453 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()"); 06454 } 06455 06456 return 0; 06457 }
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 6659 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.
06660 { 06661 int penalty; 06662 AST_DECLARE_APP_ARGS(args, 06663 AST_APP_ARG(queuename); 06664 AST_APP_ARG(interface); 06665 ); 06666 /* Make sure the returned value on error is NULL. */ 06667 buf[0] = '\0'; 06668 06669 if (ast_strlen_zero(data)) { 06670 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06671 return -1; 06672 } 06673 06674 AST_STANDARD_APP_ARGS(args, data); 06675 06676 if (args.argc < 2) { 06677 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06678 return -1; 06679 } 06680 06681 penalty = get_member_penalty (args.queuename, args.interface); 06682 06683 if (penalty >= 0) /* remember that buf is already '\0' */ 06684 snprintf (buf, len, "%d", penalty); 06685 06686 return 0; 06687 }
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 6690 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().
06691 { 06692 int penalty; 06693 AST_DECLARE_APP_ARGS(args, 06694 AST_APP_ARG(queuename); 06695 AST_APP_ARG(interface); 06696 ); 06697 06698 if (ast_strlen_zero(data)) { 06699 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06700 return -1; 06701 } 06702 06703 AST_STANDARD_APP_ARGS(args, data); 06704 06705 if (args.argc < 2) { 06706 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06707 return -1; 06708 } 06709 06710 penalty = atoi(value); 06711 06712 if (ast_strlen_zero(args.interface)) { 06713 ast_log (LOG_ERROR, "<interface> parameter can't be null\n"); 06714 return -1; 06715 } 06716 06717 /* if queuename = NULL then penalty will be set for interface in all the queues. */ 06718 if (set_member_penalty(args.queuename, args.interface, penalty)) { 06719 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n"); 06720 return -1; 06721 } 06722 06723 return 0; 06724 }
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 6464 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.
06465 { 06466 int count = 0; 06467 struct member *m; 06468 struct ao2_iterator mem_iter; 06469 struct call_queue *q; 06470 char *option; 06471 06472 if (ast_strlen_zero(data)) { 06473 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06474 return -1; 06475 } 06476 06477 if ((option = strchr(data, ','))) 06478 *option++ = '\0'; 06479 else 06480 option = "logged"; 06481 if ((q = load_realtime_queue(data))) { 06482 ao2_lock(q); 06483 if (!strcasecmp(option, "logged")) { 06484 mem_iter = ao2_iterator_init(q->members, 0); 06485 while ((m = ao2_iterator_next(&mem_iter))) { 06486 /* Count the agents who are logged in and presently answering calls */ 06487 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 06488 count++; 06489 } 06490 ao2_ref(m, -1); 06491 } 06492 ao2_iterator_destroy(&mem_iter); 06493 } else if (!strcasecmp(option, "free")) { 06494 mem_iter = ao2_iterator_init(q->members, 0); 06495 while ((m = ao2_iterator_next(&mem_iter))) { 06496 /* Count the agents who are logged in and presently answering calls */ 06497 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) { 06498 count++; 06499 } 06500 ao2_ref(m, -1); 06501 } 06502 ao2_iterator_destroy(&mem_iter); 06503 } else if (!strcasecmp(option, "ready")) { 06504 time_t now; 06505 time(&now); 06506 mem_iter = ao2_iterator_init(q->members, 0); 06507 while ((m = ao2_iterator_next(&mem_iter))) { 06508 /* Count the agents who are logged in, not paused and not wrapping up */ 06509 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) && 06510 !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) { 06511 count++; 06512 } 06513 ao2_ref(m, -1); 06514 } 06515 ao2_iterator_destroy(&mem_iter); 06516 } else /* must be "count" */ 06517 count = ao2_container_count(q->members); 06518 ao2_unlock(q); 06519 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()"); 06520 } else 06521 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06522 06523 snprintf(buf, len, "%d", count); 06524 06525 return 0; 06526 }
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 6533 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.
06534 { 06535 int count = 0; 06536 struct member *m; 06537 struct call_queue *q; 06538 struct ao2_iterator mem_iter; 06539 static int depflag = 1; 06540 06541 if (depflag) { 06542 depflag = 0; 06543 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"); 06544 } 06545 06546 if (ast_strlen_zero(data)) { 06547 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06548 return -1; 06549 } 06550 06551 if ((q = load_realtime_queue(data))) { 06552 ao2_lock(q); 06553 mem_iter = ao2_iterator_init(q->members, 0); 06554 while ((m = ao2_iterator_next(&mem_iter))) { 06555 /* Count the agents who are logged in and presently answering calls */ 06556 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 06557 count++; 06558 } 06559 ao2_ref(m, -1); 06560 } 06561 ao2_iterator_destroy(&mem_iter); 06562 ao2_unlock(q); 06563 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT"); 06564 } else 06565 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06566 06567 snprintf(buf, len, "%d", count); 06568 06569 return 0; 06570 }
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 6609 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.
06610 { 06611 struct call_queue *q, tmpq = { 06612 .name = data, 06613 }; 06614 struct member *m; 06615 06616 /* Ensure an otherwise empty list doesn't return garbage */ 06617 buf[0] = '\0'; 06618 06619 if (ast_strlen_zero(data)) { 06620 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 06621 return -1; 06622 } 06623 06624 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) { 06625 int buflen = 0, count = 0; 06626 struct ao2_iterator mem_iter; 06627 06628 ao2_lock(q); 06629 mem_iter = ao2_iterator_init(q->members, 0); 06630 while ((m = ao2_iterator_next(&mem_iter))) { 06631 /* strcat() is always faster than printf() */ 06632 if (count++) { 06633 strncat(buf + buflen, ",", len - buflen - 1); 06634 buflen++; 06635 } 06636 strncat(buf + buflen, m->interface, len - buflen - 1); 06637 buflen += strlen(m->interface); 06638 /* Safeguard against overflow (negative length) */ 06639 if (buflen >= len - 2) { 06640 ao2_ref(m, -1); 06641 ast_log(LOG_WARNING, "Truncating list\n"); 06642 break; 06643 } 06644 ao2_ref(m, -1); 06645 } 06646 ao2_iterator_destroy(&mem_iter); 06647 ao2_unlock(q); 06648 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()"); 06649 } else 06650 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06651 06652 /* We should already be terminated, but let's make sure. */ 06653 buf[len - 1] = '\0'; 06654 06655 return 0; 06656 }
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 6573 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.
06574 { 06575 int count = 0; 06576 struct call_queue *q, tmpq = { 06577 .name = data, 06578 }; 06579 struct ast_variable *var = NULL; 06580 06581 buf[0] = '\0'; 06582 06583 if (ast_strlen_zero(data)) { 06584 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n"); 06585 return -1; 06586 } 06587 06588 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) { 06589 ao2_lock(q); 06590 count = q->count; 06591 ao2_unlock(q); 06592 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()"); 06593 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) { 06594 /* if the queue is realtime but was not found in memory, this 06595 * means that the queue had been deleted from memory since it was 06596 * "dead." This means it has a 0 waiting count 06597 */ 06598 count = 0; 06599 ast_variables_destroy(var); 06600 } else 06601 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06602 06603 snprintf(buf, len, "%d", count); 06604 06605 return 0; 06606 }
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 6393 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.
06394 { 06395 int res = -1; 06396 struct call_queue *q, tmpq = { 06397 .name = data, 06398 }; 06399 06400 char interfacevar[256] = ""; 06401 float sl = 0; 06402 06403 if (ast_strlen_zero(data)) { 06404 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06405 return -1; 06406 } 06407 06408 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) { 06409 ao2_lock(q); 06410 if (q->setqueuevar) { 06411 sl = 0; 06412 res = 0; 06413 06414 if (q->callscompleted > 0) { 06415 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 06416 } 06417 06418 snprintf(interfacevar, sizeof(interfacevar), 06419 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 06420 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 06421 06422 pbx_builtin_setvar_multiple(chan, interfacevar); 06423 } 06424 06425 ao2_unlock(q); 06426 queue_t_unref(q, "Done with QUEUE() function"); 06427 } else { 06428 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06429 } 06430 06431 snprintf(buf, len, "%d", res); 06432 06433 return 0; 06434 }
static int queue_hash_cb | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1252 of file app_queue.c.
References ast_str_case_hash().
Referenced by load_module().
01253 { 01254 const struct call_queue *q = obj; 01255 01256 return ast_str_case_hash(q->name); 01257 }
static int queue_member_decrement_followers | ( | void * | obj, | |
void * | arg, | |||
int | flag | |||
) | [static] |
Definition at line 1270 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 1306 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().
01307 { 01308 int pos = mem->queuepos; 01309 01310 /* 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 01311 * who would have been next otherwise. */ 01312 if (pos < queue->rrpos) { 01313 queue->rrpos--; 01314 } 01315 01316 ao2_callback(queue->members, OBJ_NODATA | OBJ_MULTIPLE, queue_member_decrement_followers, &pos); 01317 }
static struct call_queue* queue_ref | ( | struct call_queue * | q | ) | [static, read] |
Definition at line 1331 of file app_queue.c.
References ao2_ref.
Referenced by insert_entry().
01332 { 01333 ao2_ref(q, 1); 01334 return q; 01335 }
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 6817 of file app_queue.c.
References ast_true(), and ast_variable_retrieve().
Referenced by reload_queues().
06818 { 06819 const char *general_val = NULL; 06820 queue_persistent_members = 0; 06821 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 06822 queue_persistent_members = ast_true(general_val); 06823 autofill_default = 0; 06824 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 06825 autofill_default = ast_true(general_val); 06826 montype_default = 0; 06827 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) { 06828 if (!strcasecmp(general_val, "mixmonitor")) 06829 montype_default = 1; 06830 } 06831 update_cdr = 0; 06832 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) 06833 update_cdr = ast_true(general_val); 06834 shared_lastcall = 0; 06835 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) 06836 shared_lastcall = ast_true(general_val); 06837 }
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 1942 of file app_queue.c.
References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, 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::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().
01943 { 01944 if (!strcasecmp(param, "musicclass") || 01945 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 01946 ast_string_field_set(q, moh, val); 01947 } else if (!strcasecmp(param, "announce")) { 01948 ast_string_field_set(q, announce, val); 01949 } else if (!strcasecmp(param, "context")) { 01950 ast_string_field_set(q, context, val); 01951 } else if (!strcasecmp(param, "timeout")) { 01952 q->timeout = atoi(val); 01953 if (q->timeout < 0) 01954 q->timeout = DEFAULT_TIMEOUT; 01955 } else if (!strcasecmp(param, "ringinuse")) { 01956 q->ringinuse = ast_true(val); 01957 } else if (!strcasecmp(param, "setinterfacevar")) { 01958 q->setinterfacevar = ast_true(val); 01959 } else if (!strcasecmp(param, "setqueuevar")) { 01960 q->setqueuevar = ast_true(val); 01961 } else if (!strcasecmp(param, "setqueueentryvar")) { 01962 q->setqueueentryvar = ast_true(val); 01963 } else if (!strcasecmp(param, "monitor-format")) { 01964 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 01965 } else if (!strcasecmp(param, "membermacro")) { 01966 ast_string_field_set(q, membermacro, val); 01967 } else if (!strcasecmp(param, "membergosub")) { 01968 ast_string_field_set(q, membergosub, val); 01969 } else if (!strcasecmp(param, "queue-youarenext")) { 01970 ast_string_field_set(q, sound_next, val); 01971 } else if (!strcasecmp(param, "queue-thereare")) { 01972 ast_string_field_set(q, sound_thereare, val); 01973 } else if (!strcasecmp(param, "queue-callswaiting")) { 01974 ast_string_field_set(q, sound_calls, val); 01975 } else if (!strcasecmp(param, "queue-quantity1")) { 01976 ast_string_field_set(q, queue_quantity1, val); 01977 } else if (!strcasecmp(param, "queue-quantity2")) { 01978 ast_string_field_set(q, queue_quantity2, val); 01979 } else if (!strcasecmp(param, "queue-holdtime")) { 01980 ast_string_field_set(q, sound_holdtime, val); 01981 } else if (!strcasecmp(param, "queue-minutes")) { 01982 ast_string_field_set(q, sound_minutes, val); 01983 } else if (!strcasecmp(param, "queue-minute")) { 01984 ast_string_field_set(q, sound_minute, val); 01985 } else if (!strcasecmp(param, "queue-seconds")) { 01986 ast_string_field_set(q, sound_seconds, val); 01987 } else if (!strcasecmp(param, "queue-thankyou")) { 01988 ast_string_field_set(q, sound_thanks, val); 01989 } else if (!strcasecmp(param, "queue-callerannounce")) { 01990 ast_string_field_set(q, sound_callerannounce, val); 01991 } else if (!strcasecmp(param, "queue-reporthold")) { 01992 ast_string_field_set(q, sound_reporthold, val); 01993 } else if (!strcasecmp(param, "announce-frequency")) { 01994 q->announcefrequency = atoi(val); 01995 } else if (!strcasecmp(param, "min-announce-frequency")) { 01996 q->minannouncefrequency = atoi(val); 01997 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name); 01998 } else if (!strcasecmp(param, "announce-round-seconds")) { 01999 q->roundingseconds = atoi(val); 02000 /* Rounding to any other values just doesn't make sense... */ 02001 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10 02002 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) { 02003 if (linenum >= 0) { 02004 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 02005 "using 0 instead for queue '%s' at line %d of queues.conf\n", 02006 val, param, q->name, linenum); 02007 } else { 02008 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 02009 "using 0 instead for queue '%s'\n", val, param, q->name); 02010 } 02011 q->roundingseconds=0; 02012 } 02013 } else if (!strcasecmp(param, "announce-holdtime")) { 02014 if (!strcasecmp(val, "once")) 02015 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 02016 else if (ast_true(val)) 02017 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 02018 else 02019 q->announceholdtime = 0; 02020 } else if (!strcasecmp(param, "announce-position")) { 02021 if (!strcasecmp(val, "limit")) 02022 q->announceposition = ANNOUNCEPOSITION_LIMIT; 02023 else if (!strcasecmp(val, "more")) 02024 q->announceposition = ANNOUNCEPOSITION_MORE_THAN; 02025 else if (ast_true(val)) 02026 q->announceposition = ANNOUNCEPOSITION_YES; 02027 else 02028 q->announceposition = ANNOUNCEPOSITION_NO; 02029 } else if (!strcasecmp(param, "announce-position-limit")) { 02030 q->announcepositionlimit = atoi(val); 02031 } else if (!strcasecmp(param, "periodic-announce")) { 02032 if (strchr(val, ',')) { 02033 char *s, *buf = ast_strdupa(val); 02034 unsigned int i = 0; 02035 02036 while ((s = strsep(&buf, ",|"))) { 02037 if (!q->sound_periodicannounce[i]) 02038 q->sound_periodicannounce[i] = ast_str_create(16); 02039 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s); 02040 i++; 02041 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 02042 break; 02043 } 02044 q->numperiodicannounce = i; 02045 } else { 02046 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val); 02047 q->numperiodicannounce = 1; 02048 } 02049 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 02050 q->periodicannouncefrequency = atoi(val); 02051 } else if (!strcasecmp(param, "relative-periodic-announce")) { 02052 q->relativeperiodicannounce = ast_true(val); 02053 } else if (!strcasecmp(param, "random-periodic-announce")) { 02054 q->randomperiodicannounce = ast_true(val); 02055 } else if (!strcasecmp(param, "retry")) { 02056 q->retry = atoi(val); 02057 if (q->retry <= 0) 02058 q->retry = DEFAULT_RETRY; 02059 } else if (!strcasecmp(param, "wrapuptime")) { 02060 q->wrapuptime = atoi(val); 02061 } else if (!strcasecmp(param, "penaltymemberslimit")) { 02062 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) { 02063 q->penaltymemberslimit = 0; 02064 } 02065 } else if (!strcasecmp(param, "autofill")) { 02066 q->autofill = ast_true(val); 02067 } else if (!strcasecmp(param, "monitor-type")) { 02068 if (!strcasecmp(val, "mixmonitor")) 02069 q->montype = 1; 02070 } else if (!strcasecmp(param, "autopause")) { 02071 q->autopause = autopause2int(val); 02072 } else if (!strcasecmp(param, "maxlen")) { 02073 q->maxlen = atoi(val); 02074 if (q->maxlen < 0) 02075 q->maxlen = 0; 02076 } else if (!strcasecmp(param, "servicelevel")) { 02077 q->servicelevel= atoi(val); 02078 } else if (!strcasecmp(param, "strategy")) { 02079 int strategy; 02080 02081 /* We are a static queue and already have set this, no need to do it again */ 02082 if (failunknown) { 02083 return; 02084 } 02085 strategy = strat2int(val); 02086 if (strategy < 0) { 02087 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 02088 val, q->name); 02089 q->strategy = QUEUE_STRATEGY_RINGALL; 02090 } 02091 if (strategy == q->strategy) { 02092 return; 02093 } 02094 if (strategy == QUEUE_STRATEGY_LINEAR) { 02095 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n"); 02096 return; 02097 } 02098 q->strategy = strategy; 02099 } else if (!strcasecmp(param, "joinempty")) { 02100 parse_empty_options(val, &q->joinempty, 1); 02101 } else if (!strcasecmp(param, "leavewhenempty")) { 02102 parse_empty_options(val, &q->leavewhenempty, 0); 02103 } else if (!strcasecmp(param, "eventmemberstatus")) { 02104 q->maskmemberstatus = !ast_true(val); 02105 } else if (!strcasecmp(param, "eventwhencalled")) { 02106 if (!strcasecmp(val, "vars")) { 02107 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 02108 } else { 02109 q->eventwhencalled = ast_true(val) ? 1 : 0; 02110 } 02111 } else if (!strcasecmp(param, "reportholdtime")) { 02112 q->reportholdtime = ast_true(val); 02113 } else if (!strcasecmp(param, "memberdelay")) { 02114 q->memberdelay = atoi(val); 02115 } else if (!strcasecmp(param, "weight")) { 02116 q->weight = atoi(val); 02117 } else if (!strcasecmp(param, "timeoutrestart")) { 02118 q->timeoutrestart = ast_true(val); 02119 } else if (!strcasecmp(param, "defaultrule")) { 02120 ast_string_field_set(q, defaultrule, val); 02121 } else if (!strcasecmp(param, "timeoutpriority")) { 02122 if (!strcasecmp(val, "conf")) { 02123 q->timeoutpriority = TIMEOUT_PRIORITY_CONF; 02124 } else { 02125 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 02126 } 02127 } else if (failunknown) { 02128 if (linenum >= 0) { 02129 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 02130 q->name, param, linenum); 02131 } else { 02132 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 02133 } 02134 } 02135 }
static char* queue_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7472 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.
07473 { 07474 switch ( cmd ) { 07475 case CLI_INIT: 07476 e->command = "queue show"; 07477 e->usage = 07478 "Usage: queue show\n" 07479 " Provides summary information on a specified queue.\n"; 07480 return NULL; 07481 case CLI_GENERATE: 07482 return complete_queue_show(a->line, a->word, a->pos, a->n); 07483 } 07484 07485 return __queues_show(NULL, a->fd, a->argc, a->argv); 07486 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 4451 of file app_queue.c.
References ast_free.
04452 { 04453 struct queue_transfer_ds *qtds = data; 04454 ast_free(qtds); 04455 }
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 4474 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().
04475 { 04476 struct queue_transfer_ds *qtds = data; 04477 struct queue_ent *qe = qtds->qe; 04478 struct member *member = qtds->member; 04479 time_t callstart = qtds->starttime; 04480 int callcompletedinsl = qtds->callcompletedinsl; 04481 struct ast_datastore *datastore; 04482 04483 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 04484 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 04485 (long) (time(NULL) - callstart), qe->opos); 04486 04487 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 04488 04489 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 04490 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 04491 ast_channel_datastore_remove(old_chan, datastore); 04492 } else { 04493 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 04494 } 04495 }
static struct call_queue* queue_unref | ( | struct call_queue * | q | ) | [static, read] |
Definition at line 1337 of file app_queue.c.
References ao2_ref.
Referenced by queue_exec(), and queues_data_provider_get().
01338 { 01339 ao2_ref(q, -1); 01340 return NULL; 01341 }
static int queues_data_provider_get | ( | const struct ast_data_search * | search, | |
struct ast_data * | data_root | |||
) | [static] |
Definition at line 8640 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.
08642 { 08643 struct ao2_iterator i; 08644 struct call_queue *queue, *queue_realtime = NULL; 08645 struct ast_config *cfg; 08646 char *queuename; 08647 08648 /* load realtime queues. */ 08649 cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 08650 if (cfg) { 08651 for (queuename = ast_category_browse(cfg, NULL); 08652 !ast_strlen_zero(queuename); 08653 queuename = ast_category_browse(cfg, queuename)) { 08654 if ((queue = load_realtime_queue(queuename))) { 08655 queue_unref(queue); 08656 } 08657 } 08658 ast_config_destroy(cfg); 08659 } 08660 08661 /* static queues. */ 08662 i = ao2_iterator_init(queues, 0); 08663 while ((queue = ao2_iterator_next(&i))) { 08664 ao2_lock(queue); 08665 if (queue->realtime) { 08666 queue_realtime = load_realtime_queue(queue->name); 08667 if (!queue_realtime) { 08668 ao2_unlock(queue); 08669 queue_unref(queue); 08670 continue; 08671 } 08672 queue_unref(queue_realtime); 08673 } 08674 08675 queues_data_provider_get_helper(search, data_root, queue); 08676 ao2_unlock(queue); 08677 queue_unref(queue); 08678 } 08679 ao2_iterator_destroy(&i); 08680 08681 return 0; 08682 }
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 8534 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().
08536 { 08537 struct ao2_iterator im; 08538 struct member *member; 08539 struct queue_ent *qe; 08540 struct ast_data *data_queue, *data_members = NULL, *enum_node; 08541 struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel; 08542 08543 data_queue = ast_data_add_node(data_root, "queue"); 08544 if (!data_queue) { 08545 return; 08546 } 08547 08548 ast_data_add_structure(call_queue, data_queue, queue); 08549 08550 ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy)); 08551 ast_data_add_int(data_queue, "membercount", ao2_container_count(queue->members)); 08552 08553 /* announce position */ 08554 enum_node = ast_data_add_node(data_queue, "announceposition"); 08555 if (!enum_node) { 08556 return; 08557 } 08558 switch (queue->announceposition) { 08559 case ANNOUNCEPOSITION_LIMIT: 08560 ast_data_add_str(enum_node, "text", "limit"); 08561 break; 08562 case ANNOUNCEPOSITION_MORE_THAN: 08563 ast_data_add_str(enum_node, "text", "more"); 08564 break; 08565 case ANNOUNCEPOSITION_YES: 08566 ast_data_add_str(enum_node, "text", "yes"); 08567 break; 08568 case ANNOUNCEPOSITION_NO: 08569 ast_data_add_str(enum_node, "text", "no"); 08570 break; 08571 default: 08572 ast_data_add_str(enum_node, "text", "unknown"); 08573 break; 08574 } 08575 ast_data_add_int(enum_node, "value", queue->announceposition); 08576 08577 /* add queue members */ 08578 im = ao2_iterator_init(queue->members, 0); 08579 while ((member = ao2_iterator_next(&im))) { 08580 if (!data_members) { 08581 data_members = ast_data_add_node(data_queue, "members"); 08582 if (!data_members) { 08583 ao2_ref(member, -1); 08584 continue; 08585 } 08586 } 08587 08588 data_member = ast_data_add_node(data_members, "member"); 08589 if (!data_member) { 08590 ao2_ref(member, -1); 08591 continue; 08592 } 08593 08594 ast_data_add_structure(member, data_member, member); 08595 08596 ao2_ref(member, -1); 08597 } 08598 ao2_iterator_destroy(&im); 08599 08600 /* include the callers inside the result. */ 08601 if (queue->head) { 08602 for (qe = queue->head; qe; qe = qe->next) { 08603 if (!data_callers) { 08604 data_callers = ast_data_add_node(data_queue, "callers"); 08605 if (!data_callers) { 08606 continue; 08607 } 08608 } 08609 08610 data_caller = ast_data_add_node(data_callers, "caller"); 08611 if (!data_caller) { 08612 continue; 08613 } 08614 08615 ast_data_add_structure(queue_ent, data_caller, qe); 08616 08617 /* add the caller channel. */ 08618 data_caller_channel = ast_data_add_node(data_caller, "channel"); 08619 if (!data_caller_channel) { 08620 continue; 08621 } 08622 08623 ast_channel_data_add_structure(data_caller_channel, qe->chan, 1); 08624 } 08625 } 08626 08627 /* if this queue doesn't match remove the added queue. */ 08628 if (!ast_data_search_match(search, data_queue)) { 08629 ast_data_remove_node(data_root, data_queue); 08630 } 08631 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 2836 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::holdtime, and queue_ent::parent.
Referenced by try_calling().
02837 { 02838 int oldvalue; 02839 02840 /* Calculate holdtime using an exponential average */ 02841 /* Thanks to SRT for this contribution */ 02842 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 02843 02844 ao2_lock(qe->parent); 02845 oldvalue = qe->parent->holdtime; 02846 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 02847 ao2_unlock(qe->parent); 02848 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Record that a caller gave up on waiting in queue.
Definition at line 3524 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().
03525 { 03526 set_queue_variables(qe->parent, qe->chan); 03527 ao2_lock(qe->parent); 03528 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 03529 "Queue: %s\r\n" 03530 "Uniqueid: %s\r\n" 03531 "Position: %d\r\n" 03532 "OriginalPosition: %d\r\n" 03533 "HoldTime: %d\r\n", 03534 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 03535 03536 qe->parent->callsabandoned++; 03537 ao2_unlock(qe->parent); 03538 }
static int reload | ( | void | ) | [static] |
Definition at line 8814 of file app_queue.c.
References AST_FLAGS_ALL, ast_unload_realtime(), QUEUE_RESET_STATS, and reload_handler().
08815 { 08816 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,}; 08817 ast_unload_realtime("queue_members"); 08818 reload_handler(1, &mask, NULL); 08819 return 0; 08820 }
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 7193 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().
07194 { 07195 int res = 0; 07196 07197 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) { 07198 res |= reload_queue_rules(reload); 07199 } 07200 if (ast_test_flag(mask, QUEUE_RESET_STATS)) { 07201 res |= clear_stats(queuename); 07202 } 07203 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) { 07204 res |= reload_queues(reload, mask, queuename); 07205 } 07206 return res; 07207 }
static void reload_queue_members | ( | void | ) | [static] |
Reload dynamic queue members persisted into the astdb.
Definition at line 5734 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().
05735 { 05736 char *cur_ptr; 05737 const char *queue_name; 05738 char *member; 05739 char *interface; 05740 char *membername = NULL; 05741 char *state_interface; 05742 char *penalty_tok; 05743 int penalty = 0; 05744 char *paused_tok; 05745 int paused = 0; 05746 struct ast_db_entry *db_tree; 05747 struct ast_db_entry *entry; 05748 struct call_queue *cur_queue; 05749 char *queue_data; 05750 05751 /* Each key in 'pm_family' is the name of a queue */ 05752 db_tree = ast_db_gettree(pm_family, NULL); 05753 for (entry = db_tree; entry; entry = entry->next) { 05754 05755 queue_name = entry->key + strlen(pm_family) + 2; 05756 05757 { 05758 struct call_queue tmpq = { 05759 .name = queue_name, 05760 }; 05761 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members"); 05762 } 05763 05764 if (!cur_queue) 05765 cur_queue = load_realtime_queue(queue_name); 05766 05767 if (!cur_queue) { 05768 /* If the queue no longer exists, remove it from the 05769 * database */ 05770 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 05771 ast_db_del(pm_family, queue_name); 05772 continue; 05773 } 05774 05775 if (ast_db_get_allocated(pm_family, queue_name, &queue_data)) { 05776 queue_t_unref(cur_queue, "Expire reload reference"); 05777 continue; 05778 } 05779 05780 cur_ptr = queue_data; 05781 while ((member = strsep(&cur_ptr, ",|"))) { 05782 if (ast_strlen_zero(member)) 05783 continue; 05784 05785 interface = strsep(&member, ";"); 05786 penalty_tok = strsep(&member, ";"); 05787 paused_tok = strsep(&member, ";"); 05788 membername = strsep(&member, ";"); 05789 state_interface = strsep(&member, ";"); 05790 05791 if (!penalty_tok) { 05792 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 05793 break; 05794 } 05795 penalty = strtol(penalty_tok, NULL, 10); 05796 if (errno == ERANGE) { 05797 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 05798 break; 05799 } 05800 05801 if (!paused_tok) { 05802 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 05803 break; 05804 } 05805 paused = strtol(paused_tok, NULL, 10); 05806 if ((errno == ERANGE) || paused < 0 || paused > 1) { 05807 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 05808 break; 05809 } 05810 05811 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 05812 05813 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) { 05814 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 05815 break; 05816 } 05817 } 05818 queue_t_unref(cur_queue, "Expire reload reference"); 05819 ast_free(queue_data); 05820 } 05821 05822 if (db_tree) { 05823 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 05824 ast_db_freetree(db_tree); 05825 } 05826 }
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 6768 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().
06769 { 06770 struct ast_config *cfg; 06771 struct rule_list *rl_iter, *new_rl; 06772 struct penalty_rule *pr_iter; 06773 char *rulecat = NULL; 06774 struct ast_variable *rulevar = NULL; 06775 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 06776 06777 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) { 06778 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n"); 06779 return AST_MODULE_LOAD_SUCCESS; 06780 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 06781 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n"); 06782 return AST_MODULE_LOAD_SUCCESS; 06783 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 06784 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n"); 06785 return AST_MODULE_LOAD_SUCCESS; 06786 } 06787 06788 AST_LIST_LOCK(&rule_lists); 06789 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) { 06790 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list))) 06791 ast_free(pr_iter); 06792 ast_free(rl_iter); 06793 } 06794 while ((rulecat = ast_category_browse(cfg, rulecat))) { 06795 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) { 06796 AST_LIST_UNLOCK(&rule_lists); 06797 ast_config_destroy(cfg); 06798 return AST_MODULE_LOAD_FAILURE; 06799 } else { 06800 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name)); 06801 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list); 06802 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next) 06803 if(!strcasecmp(rulevar->name, "penaltychange")) 06804 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno); 06805 else 06806 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno); 06807 } 06808 } 06809 AST_LIST_UNLOCK(&rule_lists); 06810 06811 ast_config_destroy(cfg); 06812 06813 return AST_MODULE_LOAD_SUCCESS; 06814 }
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 7098 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().
07099 { 07100 struct ast_config *cfg; 07101 char *cat; 07102 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 07103 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 07104 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER); 07105 07106 if (!(cfg = ast_config_load("queues.conf", config_flags))) { 07107 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 07108 return -1; 07109 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 07110 return 0; 07111 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 07112 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n"); 07113 return -1; 07114 } 07115 07116 /* We've made it here, so it looks like we're doing operations on all queues. */ 07117 ao2_lock(queues); 07118 07119 /* Mark all queues as dead for the moment if we're reloading queues. 07120 * For clarity, we could just be reloading members, in which case we don't want to mess 07121 * with the other queue parameters at all*/ 07122 if (queue_reload) { 07123 ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename); 07124 } 07125 07126 if (member_reload) { 07127 ao2_callback(queues, OBJ_NODATA, remove_members_and_mark_unfound, (char *) queuename); 07128 } 07129 07130 /* Chug through config file */ 07131 cat = NULL; 07132 while ((cat = ast_category_browse(cfg, cat)) ) { 07133 if (!strcasecmp(cat, "general") && queue_reload) { 07134 queue_set_global_params(cfg); 07135 continue; 07136 } 07137 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename)) 07138 reload_single_queue(cfg, mask, cat); 07139 } 07140 07141 ast_config_destroy(cfg); 07142 /* Unref all the dead queues if we were reloading queues */ 07143 if (queue_reload) { 07144 ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename); 07145 } 07146 ao2_unlock(queues); 07147 return 0; 07148 }
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 6847 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().
06848 { 06849 char *membername, *interface, *state_interface, *tmp; 06850 char *parse; 06851 struct member *cur, *newm; 06852 struct member tmpmem; 06853 int penalty; 06854 AST_DECLARE_APP_ARGS(args, 06855 AST_APP_ARG(interface); 06856 AST_APP_ARG(penalty); 06857 AST_APP_ARG(membername); 06858 AST_APP_ARG(state_interface); 06859 ); 06860 06861 if (ast_strlen_zero(memberdata)) { 06862 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n"); 06863 return; 06864 } 06865 06866 /* Add a new member */ 06867 parse = ast_strdupa(memberdata); 06868 06869 AST_STANDARD_APP_ARGS(args, parse); 06870 06871 interface = args.interface; 06872 if (!ast_strlen_zero(args.penalty)) { 06873 tmp = args.penalty; 06874 ast_strip(tmp); 06875 penalty = atoi(tmp); 06876 if (penalty < 0) { 06877 penalty = 0; 06878 } 06879 } else { 06880 penalty = 0; 06881 } 06882 06883 if (!ast_strlen_zero(args.membername)) { 06884 membername = args.membername; 06885 ast_strip(membername); 06886 } else { 06887 membername = interface; 06888 } 06889 06890 if (!ast_strlen_zero(args.state_interface)) { 06891 state_interface = args.state_interface; 06892 ast_strip(state_interface); 06893 } else { 06894 state_interface = interface; 06895 } 06896 06897 /* Find the old position in the list */ 06898 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 06899 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER); 06900 06901 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) { 06902 if (cur) { 06903 /* Round Robin Queue Position must be copied if this is replacing an existing member */ 06904 ao2_lock(q->members); 06905 newm->queuepos = cur->queuepos; 06906 ao2_link(q->members, newm); 06907 ao2_unlink(q->members, cur); 06908 ao2_unlock(q->members); 06909 } else { 06910 /* Otherwise we need to add using the function that will apply a round robin queue position manually. */ 06911 member_add_to_queue(q, newm); 06912 } 06913 ao2_ref(newm, -1); 06914 } 06915 newm = NULL; 06916 06917 if (cur) { 06918 ao2_ref(cur, -1); 06919 } 06920 }
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 6954 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().
06955 { 06956 int new; 06957 struct call_queue *q = NULL; 06958 /*We're defining a queue*/ 06959 struct call_queue tmpq = { 06960 .name = queuename, 06961 }; 06962 const char *tmpvar; 06963 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 06964 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER); 06965 int prev_weight = 0; 06966 struct ast_variable *var; 06967 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) { 06968 if (queue_reload) { 06969 /* Make one then */ 06970 if (!(q = alloc_queue(queuename))) { 06971 return; 06972 } 06973 } else { 06974 /* Since we're not reloading queues, this means that we found a queue 06975 * in the configuration file which we don't know about yet. Just return. 06976 */ 06977 return; 06978 } 06979 new = 1; 06980 } else { 06981 new = 0; 06982 } 06983 06984 if (!new) { 06985 ao2_lock(q); 06986 prev_weight = q->weight ? 1 : 0; 06987 } 06988 /* Check if we already found a queue with this name in the config file */ 06989 if (q->found) { 06990 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename); 06991 if (!new) { 06992 /* It should be impossible to *not* hit this case*/ 06993 ao2_unlock(q); 06994 } 06995 queue_t_unref(q, "We exist! Expiring temporary pointer"); 06996 return; 06997 } 06998 /* Due to the fact that the "linear" strategy will have a different allocation 06999 * scheme for queue members, we must devise the queue's strategy before other initializations. 07000 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2 07001 * container used will have only a single bucket instead of the typical number. 07002 */ 07003 if (queue_reload) { 07004 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) { 07005 q->strategy = strat2int(tmpvar); 07006 if (q->strategy < 0) { 07007 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 07008 tmpvar, q->name); 07009 q->strategy = QUEUE_STRATEGY_RINGALL; 07010 } 07011 } else { 07012 q->strategy = QUEUE_STRATEGY_RINGALL; 07013 } 07014 init_queue(q); 07015 } 07016 if (member_reload) { 07017 ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL); 07018 q->found = 1; 07019 } 07020 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) { 07021 if (member_reload && !strcasecmp(var->name, "member")) { 07022 reload_single_member(var->value, q); 07023 } else if (queue_reload) { 07024 queue_set_param(q, var->name, var->value, var->lineno, 1); 07025 } 07026 } 07027 /* At this point, we've determined if the queue has a weight, so update use_weight 07028 * as appropriate 07029 */ 07030 if (!q->weight && prev_weight) { 07031 ast_atomic_fetchadd_int(&use_weight, -1); 07032 } 07033 else if (q->weight && !prev_weight) { 07034 ast_atomic_fetchadd_int(&use_weight, +1); 07035 } 07036 07037 /* Free remaining members marked as delme */ 07038 if (member_reload) { 07039 ao2_lock(q->members); 07040 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE, queue_delme_members_decrement_followers, q); 07041 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q); 07042 ao2_unlock(q->members); 07043 } 07044 07045 if (new) { 07046 queues_t_link(queues, q, "Add queue to container"); 07047 } else { 07048 ao2_unlock(q); 07049 } 07050 queue_t_unref(q, "Expiring creation reference"); 07051 }
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 5477 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().
05478 { 05479 struct call_queue *q, tmpq = { 05480 .name = queuename, 05481 }; 05482 struct member *mem, tmpmem; 05483 int res = RES_NOSUCHQUEUE; 05484 05485 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 05486 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) { 05487 ao2_lock(q); 05488 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 05489 /* XXX future changes should beware of this assumption!! */ 05490 if (!mem->dynamic) { 05491 ao2_ref(mem, -1); 05492 ao2_unlock(q); 05493 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference"); 05494 return RES_NOT_DYNAMIC; 05495 } 05496 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 05497 "Queue: %s\r\n" 05498 "Location: %s\r\n" 05499 "MemberName: %s\r\n", 05500 q->name, mem->interface, mem->membername); 05501 member_remove_from_queue(q, mem); 05502 ao2_ref(mem, -1); 05503 05504 if (queue_persistent_members) 05505 dump_queue_members(q); 05506 05507 res = RES_OKAY; 05508 } else { 05509 res = RES_EXISTS; 05510 } 05511 ao2_unlock(q); 05512 queue_t_unref(q, "Expiring temporary reference"); 05513 } 05514 05515 return res; 05516 }
static int remove_members_and_mark_unfound | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 7053 of file app_queue.c.
References ast_strlen_zero(), call_queue::found, and call_queue::realtime.
Referenced by reload_queues().
07054 { 07055 struct call_queue *q = obj; 07056 char *queuename = arg; 07057 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) { 07058 q->found = 0; 07059 07060 } 07061 return 0; 07062 }
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 3203 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_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, queue_ent::chan, callattempt::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, 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, queue_ent::parent, pbx_builtin_getvar_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().
03204 { 03205 int res; 03206 int status; 03207 char tech[256]; 03208 char *location; 03209 const char *macrocontext, *macroexten; 03210 03211 /* on entry here, we know that tmp->chan == NULL */ 03212 if (!can_ring_entry(qe, tmp)) { 03213 tmp->stillgoing = 0; 03214 (*busies)++; 03215 return 0; 03216 } 03217 ast_assert(qe->parent->ringinuse || tmp->member->call_pending); 03218 03219 ast_copy_string(tech, tmp->interface, sizeof(tech)); 03220 if ((location = strchr(tech, '/'))) 03221 *location++ = '\0'; 03222 else 03223 location = ""; 03224 03225 /* Request the peer */ 03226 tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status); 03227 if (!tmp->chan) { /* If we can't, just go on to the next call */ 03228 ao2_lock(qe->parent); 03229 qe->parent->rrpos++; 03230 qe->linpos++; 03231 ao2_unlock(qe->parent); 03232 03233 member_call_pending_clear(tmp->member); 03234 03235 if (qe->chan->cdr) { 03236 ast_cdr_busy(qe->chan->cdr); 03237 } 03238 tmp->stillgoing = 0; 03239 (*busies)++; 03240 return 0; 03241 } 03242 03243 ast_channel_lock_both(tmp->chan, qe->chan); 03244 03245 if (qe->cancel_answered_elsewhere) { 03246 ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); 03247 } 03248 tmp->chan->appl = "AppQueue"; 03249 tmp->chan->data = "(Outgoing Line)"; 03250 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); 03251 03252 /* If the new channel has no callerid, try to guess what it should be */ 03253 if (!tmp->chan->caller.id.number.valid) { 03254 if (qe->chan->connected.id.number.valid) { 03255 struct ast_party_caller caller; 03256 03257 ast_party_caller_set_init(&caller, &tmp->chan->caller); 03258 caller.id = qe->chan->connected.id; 03259 caller.ani = qe->chan->connected.ani; 03260 ast_channel_set_caller_event(tmp->chan, &caller, NULL); 03261 } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) { 03262 ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL); 03263 } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) { 03264 ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 03265 } 03266 tmp->dial_callerid_absent = 1; 03267 } 03268 03269 ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting); 03270 03271 tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select; 03272 03273 ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller); 03274 03275 /* Inherit specially named variables from parent channel */ 03276 ast_channel_inherit_variables(qe->chan, tmp->chan); 03277 ast_channel_datastore_inherit(qe->chan, tmp->chan); 03278 03279 /* Presense of ADSI CPE on outgoing channel follows ours */ 03280 tmp->chan->adsicpe = qe->chan->adsicpe; 03281 03282 /* Inherit context and extension */ 03283 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 03284 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); 03285 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 03286 if (!ast_strlen_zero(macroexten)) 03287 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 03288 else 03289 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 03290 if (ast_cdr_isset_unanswered()) { 03291 /* they want to see the unanswered dial attempts! */ 03292 /* set up the CDR fields on all the CDRs to give sensical information */ 03293 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name); 03294 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid); 03295 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel); 03296 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src); 03297 strcpy(tmp->chan->cdr->dst, qe->chan->exten); 03298 strcpy(tmp->chan->cdr->dcontext, qe->chan->context); 03299 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp); 03300 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata); 03301 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags; 03302 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode); 03303 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield); 03304 } 03305 03306 ast_channel_unlock(tmp->chan); 03307 ast_channel_unlock(qe->chan); 03308 03309 /* Place the call, but don't wait on the answer */ 03310 if ((res = ast_call(tmp->chan, location, 0))) { 03311 /* Again, keep going even if there's an error */ 03312 ast_verb(3, "Couldn't call %s\n", tmp->interface); 03313 do_hang(tmp); 03314 member_call_pending_clear(tmp->member); 03315 ++*busies; 03316 return 0; 03317 } 03318 03319 if (qe->parent->eventwhencalled) { 03320 char vars[2048]; 03321 03322 ast_channel_lock_both(tmp->chan, qe->chan); 03323 03324 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 03325 "Queue: %s\r\n" 03326 "AgentCalled: %s\r\n" 03327 "AgentName: %s\r\n" 03328 "ChannelCalling: %s\r\n" 03329 "DestinationChannel: %s\r\n" 03330 "CallerIDNum: %s\r\n" 03331 "CallerIDName: %s\r\n" 03332 "ConnectedLineNum: %s\r\n" 03333 "ConnectedLineName: %s\r\n" 03334 "Context: %s\r\n" 03335 "Extension: %s\r\n" 03336 "Priority: %d\r\n" 03337 "Uniqueid: %s\r\n" 03338 "%s", 03339 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name, 03340 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"), 03341 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 03342 S_COR(qe->chan->connected.id.number.valid, qe->chan->connected.id.number.str, "unknown"), 03343 S_COR(qe->chan->connected.id.name.valid, qe->chan->connected.id.name.str, "unknown"), 03344 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid, 03345 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03346 03347 ast_channel_unlock(tmp->chan); 03348 ast_channel_unlock(qe->chan); 03349 03350 ast_verb(3, "Called %s\n", tmp->interface); 03351 } 03352 03353 member_call_pending_clear(tmp->member); 03354 return 1; 03355 }
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 3383 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().
03384 { 03385 int ret = 0; 03386 03387 while (ret == 0) { 03388 struct callattempt *best = find_best(outgoing); 03389 if (!best) { 03390 ast_debug(1, "Nobody left to try ringing in queue\n"); 03391 break; 03392 } 03393 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 03394 struct callattempt *cur; 03395 /* Ring everyone who shares this best metric (for ringall) */ 03396 for (cur = outgoing; cur; cur = cur->q_next) { 03397 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 03398 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 03399 ret |= ring_entry(qe, cur, busies); 03400 } 03401 } 03402 } else { 03403 /* Ring just the best channel */ 03404 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric); 03405 ret = ring_entry(qe, best, busies); 03406 } 03407 03408 /* If we have timed out, break out */ 03409 if (qe->expire && (time(NULL) >= qe->expire)) { 03410 ast_debug(1, "Queue timed out while ringing members.\n"); 03411 ret = 0; 03412 break; 03413 } 03414 } 03415 03416 return ret; 03417 }
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 3541 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().
03542 { 03543 ast_verb(3, "Nobody picked up in %d ms\n", rnatime); 03544 03545 /* Stop ringing, and resume MOH if specified */ 03546 if (qe->ring_when_ringing) { 03547 ast_indicate(qe->chan, -1); 03548 ast_moh_start(qe->chan, qe->moh, NULL); 03549 } 03550 03551 if (qe->parent->eventwhencalled) { 03552 char vars[2048]; 03553 03554 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer", 03555 "Queue: %s\r\n" 03556 "Uniqueid: %s\r\n" 03557 "Channel: %s\r\n" 03558 "Member: %s\r\n" 03559 "MemberName: %s\r\n" 03560 "Ringtime: %d\r\n" 03561 "%s", 03562 qe->parent->name, 03563 qe->chan->uniqueid, 03564 qe->chan->name, 03565 interface, 03566 membername, 03567 rnatime, 03568 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03569 } 03570 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 03571 if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) { 03572 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) { 03573 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { 03574 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", 03575 interface, qe->parent->name); 03576 } else { 03577 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 03578 } 03579 } else { 03580 /* If queue autopause is mode all, just don't send any queue to stop. 03581 * the function will stop in all queues */ 03582 if (!set_member_paused("", interface, "Auto-Pause", 1)) { 03583 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n", 03584 interface, qe->parent->name); 03585 } else { 03586 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface); 03587 } 03588 } 03589 } 03590 return; 03591 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
RemoveQueueMember application.
Definition at line 5901 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().
05902 { 05903 int res=-1; 05904 char *parse, *temppos = NULL; 05905 AST_DECLARE_APP_ARGS(args, 05906 AST_APP_ARG(queuename); 05907 AST_APP_ARG(interface); 05908 ); 05909 05910 05911 if (ast_strlen_zero(data)) { 05912 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface])\n"); 05913 return -1; 05914 } 05915 05916 parse = ast_strdupa(data); 05917 05918 AST_STANDARD_APP_ARGS(args, parse); 05919 05920 if (ast_strlen_zero(args.interface)) { 05921 args.interface = ast_strdupa(chan->name); 05922 temppos = strrchr(args.interface, '-'); 05923 if (temppos) 05924 *temppos = '\0'; 05925 } 05926 05927 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface); 05928 05929 switch (remove_from_queue(args.queuename, args.interface)) { 05930 case RES_OKAY: 05931 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 05932 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 05933 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 05934 res = 0; 05935 break; 05936 case RES_EXISTS: 05937 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 05938 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 05939 res = 0; 05940 break; 05941 case RES_NOSUCHQUEUE: 05942 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 05943 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 05944 res = 0; 05945 break; 05946 case RES_NOT_DYNAMIC: 05947 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 05948 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 05949 res = 0; 05950 break; 05951 } 05952 05953 return res; 05954 }
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 2171 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().
02172 { 02173 struct member *m; 02174 struct ao2_iterator mem_iter; 02175 int penalty = 0; 02176 int paused = 0; 02177 int found = 0; 02178 02179 if (ast_strlen_zero(rt_uniqueid)) { 02180 ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL")); 02181 return; 02182 } 02183 02184 if (penalty_str) { 02185 penalty = atoi(penalty_str); 02186 if (penalty < 0) 02187 penalty = 0; 02188 } 02189 02190 if (paused_str) { 02191 paused = atoi(paused_str); 02192 if (paused < 0) 02193 paused = 0; 02194 } 02195 02196 /* Find member by realtime uniqueid and update */ 02197 mem_iter = ao2_iterator_init(q->members, 0); 02198 while ((m = ao2_iterator_next(&mem_iter))) { 02199 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) { 02200 m->dead = 0; /* Do not delete this one. */ 02201 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 02202 if (paused_str) 02203 m->paused = paused; 02204 if (strcasecmp(state_interface, m->state_interface)) { 02205 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); 02206 } 02207 m->penalty = penalty; 02208 found = 1; 02209 ao2_ref(m, -1); 02210 break; 02211 } 02212 ao2_ref(m, -1); 02213 } 02214 ao2_iterator_destroy(&mem_iter); 02215 02216 /* Create a new member */ 02217 if (!found) { 02218 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { 02219 m->dead = 0; 02220 m->realtime = 1; 02221 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 02222 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", paused ? "PAUSED" : ""); 02223 member_add_to_queue(q, m); 02224 ao2_ref(m, -1); 02225 m = NULL; 02226 } 02227 } 02228 }
static int say_periodic_announcement | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Playback announcement to queued members if period has elapsed.
Definition at line 3468 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(), and wait_our_turn().
03469 { 03470 int res = 0; 03471 time_t now; 03472 03473 /* Get the current time */ 03474 time(&now); 03475 03476 /* Check to see if it is time to announce */ 03477 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 03478 return 0; 03479 03480 /* Stop the music on hold so we can play our own file */ 03481 if (ringing) 03482 ast_indicate(qe->chan,-1); 03483 else 03484 ast_moh_stop(qe->chan); 03485 03486 ast_verb(3, "Playing periodic announcement\n"); 03487 03488 if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) { 03489 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce; 03490 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 03491 ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) { 03492 qe->last_periodic_announce_sound = 0; 03493 } 03494 03495 /* play the announcement */ 03496 res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])); 03497 03498 if (res > 0 && !valid_exit(qe, res)) 03499 res = 0; 03500 03501 /* Resume Music on Hold if the caller is going to stay in the queue */ 03502 if (!res) { 03503 if (ringing) 03504 ast_indicate(qe->chan, AST_CONTROL_RINGING); 03505 else 03506 ast_moh_start(qe->chan, qe->moh, NULL); 03507 } 03508 03509 /* update last_periodic_announce_time */ 03510 if (qe->parent->relativeperiodicannounce) 03511 time(&qe->last_periodic_announce_time); 03512 else 03513 qe->last_periodic_announce_time = now; 03514 03515 /* Update the current periodic announcement to the next announcement */ 03516 if (!qe->parent->randomperiodicannounce) { 03517 qe->last_periodic_announce_sound++; 03518 } 03519 03520 return res; 03521 }
static int say_position | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Definition at line 2692 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(), and wait_our_turn().
02693 { 02694 int res = 0, avgholdmins, avgholdsecs, announceposition = 0; 02695 int say_thanks = 1; 02696 time_t now; 02697 02698 /* Let minannouncefrequency seconds pass between the start of each position announcement */ 02699 time(&now); 02700 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) 02701 return 0; 02702 02703 /* If either our position has changed, or we are over the freq timer, say position */ 02704 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 02705 return 0; 02706 02707 if (ringing) { 02708 ast_indicate(qe->chan,-1); 02709 } else { 02710 ast_moh_stop(qe->chan); 02711 } 02712 02713 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES || 02714 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN || 02715 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT && 02716 qe->pos <= qe->parent->announcepositionlimit)) 02717 announceposition = 1; 02718 02719 02720 if (announceposition == 1) { 02721 /* Say we're next, if we are */ 02722 if (qe->pos == 1) { 02723 res = play_file(qe->chan, qe->parent->sound_next); 02724 if (res) 02725 goto playout; 02726 else 02727 goto posout; 02728 } else { 02729 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02730 /* More than Case*/ 02731 res = play_file(qe->chan, qe->parent->queue_quantity1); 02732 if (res) 02733 goto playout; 02734 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02735 if (res) 02736 goto playout; 02737 } else { 02738 /* Normal Case */ 02739 res = play_file(qe->chan, qe->parent->sound_thereare); 02740 if (res) 02741 goto playout; 02742 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02743 if (res) 02744 goto playout; 02745 } 02746 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02747 /* More than Case*/ 02748 res = play_file(qe->chan, qe->parent->queue_quantity2); 02749 if (res) 02750 goto playout; 02751 } else { 02752 res = play_file(qe->chan, qe->parent->sound_calls); 02753 if (res) 02754 goto playout; 02755 } 02756 } 02757 } 02758 /* Round hold time to nearest minute */ 02759 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 02760 02761 /* If they have specified a rounding then round the seconds as well */ 02762 if (qe->parent->roundingseconds) { 02763 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 02764 avgholdsecs *= qe->parent->roundingseconds; 02765 } else { 02766 avgholdsecs = 0; 02767 } 02768 02769 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 02770 02771 /* If the hold time is >1 min, if it's enabled, and if it's not 02772 supposed to be only once and we have already said it, say it */ 02773 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 02774 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 02775 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 02776 res = play_file(qe->chan, qe->parent->sound_holdtime); 02777 if (res) 02778 goto playout; 02779 02780 if (avgholdmins >= 1) { 02781 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 02782 if (res) 02783 goto playout; 02784 02785 if (avgholdmins == 1) { 02786 res = play_file(qe->chan, qe->parent->sound_minute); 02787 if (res) 02788 goto playout; 02789 } else { 02790 res = play_file(qe->chan, qe->parent->sound_minutes); 02791 if (res) 02792 goto playout; 02793 } 02794 } 02795 if (avgholdsecs >= 1) { 02796 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 02797 if (res) 02798 goto playout; 02799 02800 res = play_file(qe->chan, qe->parent->sound_seconds); 02801 if (res) 02802 goto playout; 02803 } 02804 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) { 02805 say_thanks = 0; 02806 } 02807 02808 posout: 02809 if (qe->parent->announceposition) { 02810 ast_verb(3, "Told %s in %s their queue position (which was %d)\n", 02811 qe->chan->name, qe->parent->name, qe->pos); 02812 } 02813 if (say_thanks) { 02814 res = play_file(qe->chan, qe->parent->sound_thanks); 02815 } 02816 playout: 02817 02818 if ((res > 0 && !valid_exit(qe, res))) 02819 res = 0; 02820 02821 /* Set our last_pos indicators */ 02822 qe->last_pos = now; 02823 qe->last_pos_said = qe->pos; 02824 02825 /* Don't restart music on hold if we're about to exit the caller from the queue */ 02826 if (!res) { 02827 if (ringing) { 02828 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02829 } else { 02830 ast_moh_start(qe->chan, qe->moh, NULL); 02831 } 02832 } 02833 return res; 02834 }
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 4408 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().
04411 { 04412 const char *reason = NULL; /* silence dumb compilers */ 04413 04414 if (!qe->parent->eventwhencalled) 04415 return; 04416 04417 switch (rsn) { 04418 case CALLER: 04419 reason = "caller"; 04420 break; 04421 case AGENT: 04422 reason = "agent"; 04423 break; 04424 case TRANSFER: 04425 reason = "transfer"; 04426 break; 04427 } 04428 04429 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 04430 "Queue: %s\r\n" 04431 "Uniqueid: %s\r\n" 04432 "Channel: %s\r\n" 04433 "Member: %s\r\n" 04434 "MemberName: %s\r\n" 04435 "HoldTime: %ld\r\n" 04436 "TalkTime: %ld\r\n" 04437 "Reason: %s\r\n" 04438 "%s", 04439 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 04440 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason, 04441 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : ""); 04442 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
const char * | reason, | |||
int | paused | |||
) | [static] |
Definition at line 5576 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().
05577 { 05578 int found = 0; 05579 struct call_queue *q; 05580 struct member *mem; 05581 struct ao2_iterator queue_iter; 05582 int failed; 05583 05584 /* Special event for when all queues are paused - individual events still generated */ 05585 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 05586 if (ast_strlen_zero(queuename)) 05587 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 05588 05589 queue_iter = ao2_iterator_init(queues, 0); 05590 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) { 05591 ao2_lock(q); 05592 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 05593 if ((mem = interface_exists(q, interface))) { 05594 if (mem->paused == paused) { 05595 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 05596 } 05597 05598 failed = 0; 05599 if (mem->realtime) { 05600 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 05601 } 05602 05603 if (failed) { 05604 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface); 05605 ao2_ref(mem, -1); 05606 ao2_unlock(q); 05607 queue_t_unref(q, "Done with iterator"); 05608 continue; 05609 } 05610 found++; 05611 mem->paused = paused; 05612 05613 if (queue_persistent_members) 05614 dump_queue_members(q); 05615 05616 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, "")); 05617 05618 if (!ast_strlen_zero(reason)) { 05619 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 05620 "Queue: %s\r\n" 05621 "Location: %s\r\n" 05622 "MemberName: %s\r\n" 05623 "Paused: %d\r\n" 05624 "Reason: %s\r\n", 05625 q->name, mem->interface, mem->membername, paused, reason); 05626 } else { 05627 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 05628 "Queue: %s\r\n" 05629 "Location: %s\r\n" 05630 "MemberName: %s\r\n" 05631 "Paused: %d\r\n", 05632 q->name, mem->interface, mem->membername, paused); 05633 } 05634 ao2_ref(mem, -1); 05635 } 05636 } 05637 05638 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) { 05639 ao2_unlock(q); 05640 queue_t_unref(q, "Done with iterator"); 05641 break; 05642 } 05643 05644 ao2_unlock(q); 05645 queue_t_unref(q, "Done with iterator"); 05646 } 05647 ao2_iterator_destroy(&queue_iter); 05648 05649 return found ? RESULT_SUCCESS : RESULT_FAILURE; 05650 }
static int set_member_penalty | ( | const char * | queuename, | |
const char * | interface, | |||
int | penalty | |||
) | [static] |
Definition at line 5653 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().
05654 { 05655 int foundinterface = 0, foundqueue = 0; 05656 struct call_queue *q; 05657 struct member *mem; 05658 struct ao2_iterator queue_iter; 05659 05660 if (penalty < 0) { 05661 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); 05662 return RESULT_FAILURE; 05663 } 05664 05665 queue_iter = ao2_iterator_init(queues, 0); 05666 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 05667 ao2_lock(q); 05668 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 05669 foundqueue++; 05670 if ((mem = interface_exists(q, interface))) { 05671 foundinterface++; 05672 mem->penalty = penalty; 05673 05674 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); 05675 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", 05676 "Queue: %s\r\n" 05677 "Location: %s\r\n" 05678 "Penalty: %d\r\n", 05679 q->name, mem->interface, penalty); 05680 ao2_ref(mem, -1); 05681 } 05682 } 05683 ao2_unlock(q); 05684 queue_t_unref(q, "Done with iterator"); 05685 } 05686 ao2_iterator_destroy(&queue_iter); 05687 05688 if (foundinterface) { 05689 return RESULT_SUCCESS; 05690 } else if (!foundqueue) { 05691 ast_log (LOG_ERROR, "Invalid queuename\n"); 05692 } else { 05693 ast_log (LOG_ERROR, "Invalid interface\n"); 05694 } 05695 05696 return RESULT_FAILURE; 05697 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 1196 of file app_queue.c.
References ARRAY_LEN, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
01197 { 01198 int i; 01199 01200 for (i = 0; i < ARRAY_LEN(queue_results); i++) { 01201 if (queue_results[i].id == res) { 01202 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 01203 return; 01204 } 01205 } 01206 }
static void set_queue_variables | ( | struct call_queue * | q, | |
struct ast_channel * | chan | |||
) | [static] |
Set variables of queue.
Definition at line 1345 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().
01346 { 01347 char interfacevar[256]=""; 01348 float sl = 0; 01349 01350 ao2_lock(q); 01351 01352 if (q->setqueuevar) { 01353 sl = 0; 01354 if (q->callscompleted > 0) 01355 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 01356 01357 snprintf(interfacevar, sizeof(interfacevar), 01358 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 01359 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 01360 01361 ao2_unlock(q); 01362 01363 pbx_builtin_setvar_multiple(chan, interfacevar); 01364 } else { 01365 ao2_unlock(q); 01366 } 01367 }
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 4512 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().
04513 { 04514 struct ast_datastore *ds; 04515 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 04516 04517 if (!qtds) { 04518 ast_log(LOG_WARNING, "Memory allocation error!\n"); 04519 return NULL; 04520 } 04521 04522 ast_channel_lock(qe->chan); 04523 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) { 04524 ast_channel_unlock(qe->chan); 04525 ast_free(qtds); 04526 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 04527 return NULL; 04528 } 04529 04530 qtds->qe = qe; 04531 /* This member is refcounted in try_calling, so no need to add it here, too */ 04532 qtds->member = member; 04533 qtds->starttime = starttime; 04534 qtds->callcompletedinsl = callcompletedinsl; 04535 ds->data = qtds; 04536 ast_channel_datastore_add(qe->chan, ds); 04537 ast_channel_unlock(qe->chan); 04538 return ds; 04539 }
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 3444 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().
03445 { 03446 struct callattempt *best = find_best(outgoing); 03447 03448 if (best) { 03449 /* Ring just the best channel */ 03450 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 03451 qe->linpos = best->metric % 1000; 03452 } else { 03453 /* Just increment rrpos */ 03454 if (qe->linwrapped) { 03455 /* No more channels, start over */ 03456 qe->linpos = 0; 03457 } else { 03458 /* Prioritize next entry */ 03459 qe->linpos++; 03460 } 03461 } 03462 qe->linwrapped = 0; 03463 03464 return 0; 03465 }
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 3420 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().
03421 { 03422 struct callattempt *best = find_best(outgoing); 03423 03424 if (best) { 03425 /* Ring just the best channel */ 03426 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 03427 qe->parent->rrpos = best->metric % 1000; 03428 } else { 03429 /* Just increment rrpos */ 03430 if (qe->parent->wrapped) { 03431 /* No more channels, start over */ 03432 qe->parent->rrpos = 0; 03433 } else { 03434 /* Prioritize next entry */ 03435 qe->parent->rrpos++; 03436 } 03437 } 03438 qe->parent->wrapped = 0; 03439 03440 return 0; 03441 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 1220 of file app_queue.c.
References ARRAY_LEN, and strategies.
Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_single_queue().
01221 { 01222 int x; 01223 01224 for (x = 0; x < ARRAY_LEN(strategies); x++) { 01225 if (!strcasecmp(strategy, strategies[x].name)) 01226 return strategies[x].strategy; 01227 } 01228 01229 return -1; 01230 }
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 4593 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_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, 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(), 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, 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().
04594 { 04595 struct member *cur; 04596 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 04597 int to, orig; 04598 char oldexten[AST_MAX_EXTENSION]=""; 04599 char oldcontext[AST_MAX_CONTEXT]=""; 04600 char queuename[256]=""; 04601 char interfacevar[256]=""; 04602 struct ast_channel *peer; 04603 struct ast_channel *which; 04604 struct callattempt *lpeer; 04605 struct member *member; 04606 struct ast_app *application; 04607 int res = 0, bridge = 0; 04608 int numbusies = 0; 04609 int x=0; 04610 char *announce = NULL; 04611 char digit = 0; 04612 time_t callstart; 04613 time_t now = time(NULL); 04614 struct ast_bridge_config bridge_config; 04615 char nondataquality = 1; 04616 char *agiexec = NULL; 04617 char *macroexec = NULL; 04618 char *gosubexec = NULL; 04619 const char *monitorfilename; 04620 const char *monitor_exec; 04621 const char *monitor_options; 04622 char tmpid[256], tmpid2[256]; 04623 char meid[1024], meid2[1024]; 04624 char mixmonargs[1512]; 04625 struct ast_app *mixmonapp = NULL; 04626 char *p; 04627 char vars[2048]; 04628 int forwardsallowed = 1; 04629 int block_connected_line = 0; 04630 int callcompletedinsl; 04631 struct ao2_iterator memi; 04632 struct ast_datastore *datastore, *transfer_ds; 04633 struct queue_end_bridge *queue_end_bridge = NULL; 04634 04635 ast_channel_lock(qe->chan); 04636 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 04637 ast_channel_unlock(qe->chan); 04638 04639 memset(&bridge_config, 0, sizeof(bridge_config)); 04640 tmpid[0] = 0; 04641 meid[0] = 0; 04642 time(&now); 04643 04644 /* If we've already exceeded our timeout, then just stop 04645 * This should be extremely rare. queue_exec will take care 04646 * of removing the caller and reporting the timeout as the reason. 04647 */ 04648 if (qe->expire && now >= qe->expire) { 04649 res = 0; 04650 goto out; 04651 } 04652 04653 for (; options && *options; options++) 04654 switch (*options) { 04655 case 't': 04656 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 04657 break; 04658 case 'T': 04659 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 04660 break; 04661 case 'w': 04662 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 04663 break; 04664 case 'W': 04665 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 04666 break; 04667 case 'c': 04668 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN); 04669 break; 04670 case 'd': 04671 nondataquality = 0; 04672 break; 04673 case 'h': 04674 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 04675 break; 04676 case 'H': 04677 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 04678 break; 04679 case 'k': 04680 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL); 04681 break; 04682 case 'K': 04683 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL); 04684 break; 04685 case 'n': 04686 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) 04687 (*tries)++; 04688 else 04689 *tries = ao2_container_count(qe->parent->members); 04690 *noption = 1; 04691 break; 04692 case 'i': 04693 forwardsallowed = 0; 04694 break; 04695 case 'I': 04696 block_connected_line = 1; 04697 break; 04698 case 'x': 04699 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); 04700 break; 04701 case 'X': 04702 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON); 04703 break; 04704 case 'C': 04705 qe->cancel_answered_elsewhere = 1; 04706 break; 04707 } 04708 04709 /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 04710 (this is mainly to support chan_local) 04711 */ 04712 if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) { 04713 qe->cancel_answered_elsewhere = 1; 04714 } 04715 04716 ao2_lock(qe->parent); 04717 ast_debug(1, "%s is trying to call a queue member.\n", 04718 qe->chan->name); 04719 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 04720 if (!ast_strlen_zero(qe->announce)) 04721 announce = qe->announce; 04722 if (!ast_strlen_zero(announceoverride)) 04723 announce = announceoverride; 04724 04725 memi = ao2_iterator_init(qe->parent->members, 0); 04726 while ((cur = ao2_iterator_next(&memi))) { 04727 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 04728 struct ast_dialed_interface *di; 04729 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 04730 if (!tmp) { 04731 ao2_ref(cur, -1); 04732 ao2_iterator_destroy(&memi); 04733 ao2_unlock(qe->parent); 04734 goto out; 04735 } 04736 if (!datastore) { 04737 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { 04738 callattempt_free(tmp); 04739 ao2_ref(cur, -1); 04740 ao2_iterator_destroy(&memi); 04741 ao2_unlock(qe->parent); 04742 goto out; 04743 } 04744 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 04745 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 04746 callattempt_free(tmp); 04747 ao2_ref(cur, -1); 04748 ao2_iterator_destroy(&memi); 04749 ao2_unlock(qe->parent); 04750 goto out; 04751 } 04752 datastore->data = dialed_interfaces; 04753 AST_LIST_HEAD_INIT(dialed_interfaces); 04754 04755 ast_channel_lock(qe->chan); 04756 ast_channel_datastore_add(qe->chan, datastore); 04757 ast_channel_unlock(qe->chan); 04758 } else 04759 dialed_interfaces = datastore->data; 04760 04761 AST_LIST_LOCK(dialed_interfaces); 04762 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 04763 if (!strcasecmp(cur->interface, di->interface)) { 04764 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 04765 di->interface); 04766 break; 04767 } 04768 } 04769 AST_LIST_UNLOCK(dialed_interfaces); 04770 04771 if (di) { 04772 callattempt_free(tmp); 04773 ao2_ref(cur, -1); 04774 continue; 04775 } 04776 04777 /* It is always ok to dial a Local interface. We only keep track of 04778 * which "real" interfaces have been dialed. The Local channel will 04779 * inherit this list so that if it ends up dialing a real interface, 04780 * it won't call one that has already been called. */ 04781 if (strncasecmp(cur->interface, "Local/", 6)) { 04782 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 04783 callattempt_free(tmp); 04784 ao2_ref(cur, -1); 04785 ao2_iterator_destroy(&memi); 04786 ao2_unlock(qe->parent); 04787 goto out; 04788 } 04789 strcpy(di->interface, cur->interface); 04790 04791 AST_LIST_LOCK(dialed_interfaces); 04792 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 04793 AST_LIST_UNLOCK(dialed_interfaces); 04794 } 04795 04796 /* 04797 * Seed the callattempt's connected line information with previously 04798 * acquired connected line info from the queued channel. The 04799 * previously acquired connected line info could have been set 04800 * through the CONNECTED_LINE dialplan function. 04801 */ 04802 ast_channel_lock(qe->chan); 04803 ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); 04804 ast_channel_unlock(qe->chan); 04805 04806 tmp->block_connected_update = block_connected_line; 04807 tmp->stillgoing = 1; 04808 tmp->member = cur;/* Place the reference for cur into callattempt. */ 04809 tmp->lastcall = cur->lastcall; 04810 tmp->lastqueue = cur->lastqueue; 04811 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 04812 /* Special case: If we ring everyone, go ahead and ring them, otherwise 04813 just calculate their metric for the appropriate strategy */ 04814 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 04815 /* Put them in the list of outgoing thingies... We're ready now. 04816 XXX If we're forcibly removed, these outgoing calls won't get 04817 hung up XXX */ 04818 tmp->q_next = outgoing; 04819 outgoing = tmp; 04820 /* If this line is up, don't try anybody else */ 04821 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 04822 break; 04823 } else { 04824 callattempt_free(tmp); 04825 } 04826 } 04827 ao2_iterator_destroy(&memi); 04828 04829 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) { 04830 /* Application arguments have higher timeout priority (behaviour for <=1.6) */ 04831 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 04832 to = (qe->expire - now) * 1000; 04833 else 04834 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 04835 } else { 04836 /* Config timeout is higher priority thatn application timeout */ 04837 if (qe->expire && qe->expire<=now) { 04838 to = 0; 04839 } else if (qe->parent->timeout) { 04840 to = qe->parent->timeout * 1000; 04841 } else { 04842 to = -1; 04843 } 04844 } 04845 orig = to; 04846 ++qe->pending; 04847 ao2_unlock(qe->parent); 04848 ring_one(qe, outgoing, &numbusies); 04849 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, 04850 ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), 04851 forwardsallowed); 04852 /* The ast_channel_datastore_remove() function could fail here if the 04853 * datastore was moved to another channel during a masquerade. If this is 04854 * the case, don't free the datastore here because later, when the channel 04855 * to which the datastore was moved hangs up, it will attempt to free this 04856 * datastore again, causing a crash 04857 */ 04858 ast_channel_lock(qe->chan); 04859 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 04860 ast_datastore_free(datastore); 04861 } 04862 ast_channel_unlock(qe->chan); 04863 ao2_lock(qe->parent); 04864 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) { 04865 store_next_rr(qe, outgoing); 04866 04867 } 04868 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) { 04869 store_next_lin(qe, outgoing); 04870 } 04871 ao2_unlock(qe->parent); 04872 peer = lpeer ? lpeer->chan : NULL; 04873 if (!peer) { 04874 qe->pending = 0; 04875 if (to) { 04876 /* Must gotten hung up */ 04877 res = -1; 04878 } else { 04879 /* User exited by pressing a digit */ 04880 res = digit; 04881 } 04882 if (res == -1) 04883 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name); 04884 if (ast_cdr_isset_unanswered()) { 04885 /* channel contains the name of one of the outgoing channels 04886 in its CDR; zero out this CDR to avoid a dual-posting */ 04887 struct callattempt *o; 04888 for (o = outgoing; o; o = o->q_next) { 04889 if (!o->chan) { 04890 continue; 04891 } 04892 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) { 04893 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED); 04894 break; 04895 } 04896 } 04897 } 04898 } else { /* peer is valid */ 04899 /* Ah ha! Someone answered within the desired timeframe. Of course after this 04900 we will always return with -1 so that it is hung up properly after the 04901 conversation. */ 04902 if (!strcmp(qe->chan->tech->type, "DAHDI")) 04903 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 04904 if (!strcmp(peer->tech->type, "DAHDI")) 04905 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 04906 /* Update parameters for the queue */ 04907 time(&now); 04908 recalc_holdtime(qe, (now - qe->start)); 04909 ao2_lock(qe->parent); 04910 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 04911 ao2_unlock(qe->parent); 04912 member = lpeer->member; 04913 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 04914 ao2_ref(member, 1); 04915 hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere); 04916 outgoing = NULL; 04917 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 04918 int res2; 04919 04920 res2 = ast_autoservice_start(qe->chan); 04921 if (!res2) { 04922 if (qe->parent->memberdelay) { 04923 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 04924 res2 = ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 04925 } 04926 if (!res2 && announce) { 04927 if (play_file(peer, announce) < 0) { 04928 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name); 04929 } 04930 } 04931 if (!res2 && qe->parent->reportholdtime) { 04932 if (!play_file(peer, qe->parent->sound_reporthold)) { 04933 int holdtime, holdtimesecs; 04934 04935 time(&now); 04936 holdtime = abs((now - qe->start) / 60); 04937 holdtimesecs = abs((now - qe->start) % 60); 04938 if (holdtime > 0) { 04939 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 04940 if (play_file(peer, qe->parent->sound_minutes) < 0) { 04941 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name); 04942 } 04943 } 04944 if (holdtimesecs > 1) { 04945 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL); 04946 if (play_file(peer, qe->parent->sound_seconds) < 0) { 04947 ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name); 04948 } 04949 } 04950 } 04951 } 04952 ast_autoservice_stop(qe->chan); 04953 } 04954 if (ast_check_hangup(peer)) { 04955 /* Agent must have hung up */ 04956 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 04957 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 04958 if (qe->parent->eventwhencalled) 04959 manager_event(EVENT_FLAG_AGENT, "AgentDump", 04960 "Queue: %s\r\n" 04961 "Uniqueid: %s\r\n" 04962 "Channel: %s\r\n" 04963 "Member: %s\r\n" 04964 "MemberName: %s\r\n" 04965 "%s", 04966 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 04967 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 04968 ast_hangup(peer); 04969 ao2_ref(member, -1); 04970 goto out; 04971 } else if (ast_check_hangup(qe->chan)) { 04972 /* Caller must have hung up just before being connected */ 04973 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 04974 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 04975 record_abandoned(qe); 04976 ast_hangup(peer); 04977 ao2_ref(member, -1); 04978 return -1; 04979 } 04980 } 04981 /* Stop music on hold */ 04982 if (ringing) 04983 ast_indicate(qe->chan,-1); 04984 else 04985 ast_moh_stop(qe->chan); 04986 /* If appropriate, log that we have a destination channel */ 04987 if (qe->chan->cdr) 04988 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 04989 /* Make sure channels are compatible */ 04990 res = ast_channel_make_compatible(qe->chan, peer); 04991 if (res < 0) { 04992 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 04993 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 04994 record_abandoned(qe); 04995 ast_cdr_failed(qe->chan->cdr); 04996 ast_hangup(peer); 04997 ao2_ref(member, -1); 04998 return -1; 04999 } 05000 05001 /* Play announcement to the caller telling it's his turn if defined */ 05002 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) { 05003 if (play_file(qe->chan, qe->parent->sound_callerannounce)) 05004 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce); 05005 } 05006 05007 ao2_lock(qe->parent); 05008 /* if setinterfacevar is defined, make member variables available to the channel */ 05009 /* use pbx_builtin_setvar to set a load of variables with one call */ 05010 if (qe->parent->setinterfacevar) { 05011 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d", 05012 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime); 05013 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 05014 pbx_builtin_setvar_multiple(peer, interfacevar); 05015 } 05016 05017 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */ 05018 /* use pbx_builtin_setvar to set a load of variables with one call */ 05019 if (qe->parent->setqueueentryvar) { 05020 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d", 05021 (long) time(NULL) - qe->start, qe->opos); 05022 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 05023 pbx_builtin_setvar_multiple(peer, interfacevar); 05024 } 05025 05026 ao2_unlock(qe->parent); 05027 05028 /* try to set queue variables if configured to do so*/ 05029 set_queue_variables(qe->parent, qe->chan); 05030 set_queue_variables(qe->parent, peer); 05031 05032 ast_channel_lock(qe->chan); 05033 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) { 05034 monitorfilename = ast_strdupa(monitorfilename); 05035 } 05036 ast_channel_unlock(qe->chan); 05037 /* Begin Monitoring */ 05038 if (qe->parent->monfmt && *qe->parent->monfmt) { 05039 if (!qe->parent->montype) { 05040 const char *monexec; 05041 ast_debug(1, "Starting Monitor as requested.\n"); 05042 ast_channel_lock(qe->chan); 05043 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) { 05044 which = qe->chan; 05045 monexec = monexec ? ast_strdupa(monexec) : NULL; 05046 } 05047 else 05048 which = peer; 05049 ast_channel_unlock(qe->chan); 05050 if (monitorfilename) { 05051 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); 05052 } else if (qe->chan->cdr) { 05053 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); 05054 } else { 05055 /* Last ditch effort -- no CDR, make up something */ 05056 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 05057 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); 05058 } 05059 if (!ast_strlen_zero(monexec)) { 05060 ast_monitor_setjoinfiles(which, 1); 05061 } 05062 } else { 05063 mixmonapp = pbx_findapp("MixMonitor"); 05064 05065 if (mixmonapp) { 05066 ast_debug(1, "Starting MixMonitor as requested.\n"); 05067 if (!monitorfilename) { 05068 if (qe->chan->cdr) 05069 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)); 05070 else 05071 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 05072 } else { 05073 const char *m = monitorfilename; 05074 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) { 05075 switch (*m) { 05076 case '^': 05077 if (*(m + 1) == '{') 05078 *p = '$'; 05079 break; 05080 case ',': 05081 *p++ = '\\'; 05082 /* Fall through */ 05083 default: 05084 *p = *m; 05085 } 05086 if (*m == '\0') 05087 break; 05088 } 05089 if (p == tmpid2 + sizeof(tmpid2)) 05090 tmpid2[sizeof(tmpid2) - 1] = '\0'; 05091 05092 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 05093 } 05094 05095 ast_channel_lock(qe->chan); 05096 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) { 05097 monitor_exec = ast_strdupa(monitor_exec); 05098 } 05099 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) { 05100 monitor_options = ast_strdupa(monitor_options); 05101 } else { 05102 monitor_options = ""; 05103 } 05104 ast_channel_unlock(qe->chan); 05105 05106 if (monitor_exec) { 05107 const char *m = monitor_exec; 05108 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) { 05109 switch (*m) { 05110 case '^': 05111 if (*(m + 1) == '{') 05112 *p = '$'; 05113 break; 05114 case ',': 05115 *p++ = '\\'; 05116 /* Fall through */ 05117 default: 05118 *p = *m; 05119 } 05120 if (*m == '\0') 05121 break; 05122 } 05123 if (p == meid2 + sizeof(meid2)) 05124 meid2[sizeof(meid2) - 1] = '\0'; 05125 05126 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 05127 } 05128 05129 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt); 05130 05131 if (!ast_strlen_zero(monitor_exec)) 05132 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec); 05133 else 05134 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options); 05135 05136 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 05137 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 05138 if (qe->chan->cdr) 05139 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 05140 pbx_exec(qe->chan, mixmonapp, mixmonargs); 05141 if (qe->chan->cdr) 05142 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 05143 05144 } else { 05145 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 05146 } 05147 } 05148 } 05149 /* Drop out of the queue at this point, to prepare for next caller */ 05150 leave_queue(qe); 05151 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 05152 ast_debug(1, "app_queue: sendurl=%s.\n", url); 05153 ast_channel_sendurl(peer, url); 05154 } 05155 05156 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */ 05157 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */ 05158 if (!ast_strlen_zero(macro)) { 05159 macroexec = ast_strdupa(macro); 05160 } else { 05161 if (qe->parent->membermacro) 05162 macroexec = ast_strdupa(qe->parent->membermacro); 05163 } 05164 05165 if (!ast_strlen_zero(macroexec)) { 05166 ast_debug(1, "app_queue: macro=%s.\n", macroexec); 05167 05168 res = ast_autoservice_start(qe->chan); 05169 if (res) { 05170 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 05171 res = -1; 05172 } 05173 05174 application = pbx_findapp("Macro"); 05175 05176 if (application) { 05177 res = pbx_exec(peer, application, macroexec); 05178 ast_debug(1, "Macro exited with status %d\n", res); 05179 res = 0; 05180 } else { 05181 ast_log(LOG_ERROR, "Could not find application Macro\n"); 05182 res = -1; 05183 } 05184 05185 if (ast_autoservice_stop(qe->chan) < 0) { 05186 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 05187 res = -1; 05188 } 05189 } 05190 05191 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */ 05192 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */ 05193 if (!ast_strlen_zero(gosub)) { 05194 gosubexec = ast_strdupa(gosub); 05195 } else { 05196 if (qe->parent->membergosub) 05197 gosubexec = ast_strdupa(qe->parent->membergosub); 05198 } 05199 05200 if (!ast_strlen_zero(gosubexec)) { 05201 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec); 05202 05203 res = ast_autoservice_start(qe->chan); 05204 if (res) { 05205 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 05206 res = -1; 05207 } 05208 05209 application = pbx_findapp("Gosub"); 05210 05211 if (application) { 05212 char *gosub_args, *gosub_argstart; 05213 05214 /* Set where we came from */ 05215 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context)); 05216 ast_copy_string(peer->exten, "s", sizeof(peer->exten)); 05217 peer->priority = 0; 05218 05219 gosub_argstart = strchr(gosubexec, ','); 05220 if (gosub_argstart) { 05221 const char *what_is_s = "s"; 05222 *gosub_argstart = 0; 05223 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) && 05224 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) { 05225 what_is_s = "~~s~~"; 05226 } 05227 if (ast_asprintf(&gosub_args, "%s,%s,1(%s)", gosubexec, what_is_s, gosub_argstart + 1) < 0) { 05228 gosub_args = NULL; 05229 } 05230 *gosub_argstart = ','; 05231 } else { 05232 const char *what_is_s = "s"; 05233 if (!ast_exists_extension(peer, gosubexec, "s", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL)) && 05234 ast_exists_extension(peer, gosubexec, "~~s~~", 1, S_COR(peer->caller.id.number.valid, peer->caller.id.number.str, NULL))) { 05235 what_is_s = "~~s~~"; 05236 } 05237 if (ast_asprintf(&gosub_args, "%s,%s,1", gosubexec, what_is_s) < 0) { 05238 gosub_args = NULL; 05239 } 05240 } 05241 if (gosub_args) { 05242 res = pbx_exec(peer, application, gosub_args); 05243 if (!res) { 05244 struct ast_pbx_args args; 05245 memset(&args, 0, sizeof(args)); 05246 args.no_hangup_chan = 1; 05247 ast_pbx_run_args(peer, &args); 05248 } 05249 ast_free(gosub_args); 05250 ast_debug(1, "Gosub exited with status %d\n", res); 05251 } else { 05252 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); 05253 } 05254 } else { 05255 ast_log(LOG_ERROR, "Could not find application Gosub\n"); 05256 res = -1; 05257 } 05258 05259 if (ast_autoservice_stop(qe->chan) < 0) { 05260 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 05261 res = -1; 05262 } 05263 } 05264 05265 if (!ast_strlen_zero(agi)) { 05266 ast_debug(1, "app_queue: agi=%s.\n", agi); 05267 application = pbx_findapp("agi"); 05268 if (application) { 05269 agiexec = ast_strdupa(agi); 05270 pbx_exec(qe->chan, application, agiexec); 05271 } else 05272 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 05273 } 05274 qe->handled++; 05275 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid, 05276 (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); 05277 05278 if (qe->chan->cdr) { 05279 struct ast_cdr *cdr; 05280 struct ast_cdr *newcdr; 05281 05282 /* Only work with the last CDR in the stack*/ 05283 cdr = qe->chan->cdr; 05284 while (cdr->next) { 05285 cdr = cdr->next; 05286 } 05287 05288 /* If this CDR is not related to us add new one*/ 05289 if ((strcasecmp(cdr->uniqueid, qe->chan->uniqueid)) && 05290 (strcasecmp(cdr->linkedid, qe->chan->uniqueid)) && 05291 (newcdr = ast_cdr_dup(cdr))) { 05292 ast_channel_lock(qe->chan); 05293 ast_cdr_init(newcdr, qe->chan); 05294 ast_cdr_reset(newcdr, 0); 05295 cdr = ast_cdr_append(cdr, newcdr); 05296 cdr = cdr->next; 05297 ast_channel_unlock(qe->chan); 05298 } 05299 05300 if (update_cdr) { 05301 ast_copy_string(cdr->dstchannel, member->membername, sizeof(cdr->dstchannel)); 05302 } 05303 } 05304 05305 if (qe->parent->eventwhencalled) 05306 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 05307 "Queue: %s\r\n" 05308 "Uniqueid: %s\r\n" 05309 "Channel: %s\r\n" 05310 "Member: %s\r\n" 05311 "MemberName: %s\r\n" 05312 "Holdtime: %ld\r\n" 05313 "BridgedChannel: %s\r\n" 05314 "Ringtime: %ld\r\n" 05315 "%s", 05316 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 05317 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0), 05318 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 05319 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 05320 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 05321 05322 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { 05323 queue_end_bridge->q = qe->parent; 05324 queue_end_bridge->chan = qe->chan; 05325 bridge_config.end_bridge_callback = end_bridge_callback; 05326 bridge_config.end_bridge_callback_data = queue_end_bridge; 05327 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; 05328 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need 05329 * to make sure to increase the refcount of this queue so it cannot be freed until we 05330 * are done with it. We remove this reference in end_bridge_callback. 05331 */ 05332 queue_t_ref(qe->parent, "For bridge_config reference"); 05333 } 05334 05335 time(&callstart); 05336 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 05337 bridge = ast_bridge_call(qe->chan, peer, &bridge_config); 05338 05339 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log 05340 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for 05341 * the AgentComplete manager event 05342 */ 05343 ast_channel_lock(qe->chan); 05344 if (!attended_transfer_occurred(qe->chan)) { 05345 struct ast_datastore *tds; 05346 05347 /* detect a blind transfer */ 05348 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) { 05349 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 05350 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 05351 (long) (time(NULL) - callstart), qe->opos); 05352 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 05353 } else if (ast_check_hangup(qe->chan) && !ast_check_hangup(peer)) { 05354 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 05355 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 05356 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER); 05357 } else { 05358 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 05359 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 05360 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT); 05361 } 05362 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 05363 ast_channel_datastore_remove(qe->chan, tds); 05364 } 05365 ast_channel_unlock(qe->chan); 05366 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 05367 } else { 05368 ast_channel_unlock(qe->chan); 05369 05370 /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */ 05371 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 05372 } 05373 05374 if (transfer_ds) { 05375 ast_datastore_free(transfer_ds); 05376 } 05377 ast_hangup(peer); 05378 res = bridge ? bridge : 1; 05379 ao2_ref(member, -1); 05380 } 05381 out: 05382 hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere); 05383 05384 return res; 05385 }
static int unload_module | ( | void | ) | [static] |
Definition at line 8693 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.
08694 { 08695 int res; 08696 struct ast_context *con; 08697 struct ao2_iterator q_iter; 08698 struct call_queue *q = NULL; 08699 08700 ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue)); 08701 res = ast_manager_unregister("QueueStatus"); 08702 res |= ast_manager_unregister("Queues"); 08703 res |= ast_manager_unregister("QueueRule"); 08704 res |= ast_manager_unregister("QueueSummary"); 08705 res |= ast_manager_unregister("QueueAdd"); 08706 res |= ast_manager_unregister("QueueRemove"); 08707 res |= ast_manager_unregister("QueuePause"); 08708 res |= ast_manager_unregister("QueueLog"); 08709 res |= ast_manager_unregister("QueuePenalty"); 08710 res |= ast_unregister_application(app_aqm); 08711 res |= ast_unregister_application(app_rqm); 08712 res |= ast_unregister_application(app_pqm); 08713 res |= ast_unregister_application(app_upqm); 08714 res |= ast_unregister_application(app_ql); 08715 res |= ast_unregister_application(app); 08716 res |= ast_custom_function_unregister(&queueexists_function); 08717 res |= ast_custom_function_unregister(&queuevar_function); 08718 res |= ast_custom_function_unregister(&queuemembercount_function); 08719 res |= ast_custom_function_unregister(&queuemembercount_dep); 08720 res |= ast_custom_function_unregister(&queuememberlist_function); 08721 res |= ast_custom_function_unregister(&queuewaitingcount_function); 08722 res |= ast_custom_function_unregister(&queuememberpenalty_function); 08723 08724 res |= ast_data_unregister(NULL); 08725 08726 if (device_state_sub) 08727 ast_event_unsubscribe(device_state_sub); 08728 08729 ast_extension_state_del(0, extension_state_cb); 08730 08731 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) { 08732 ast_context_remove_extension2(con, "s", 1, NULL, 0); 08733 ast_context_destroy(con, "app_queue"); /* leave no trace */ 08734 } 08735 08736 q_iter = ao2_iterator_init(queues, 0); 08737 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) { 08738 queues_t_unlink(queues, q, "Remove queue from container due to unload"); 08739 queue_t_unref(q, "Done with iterator"); 08740 } 08741 ao2_iterator_destroy(&q_iter); 08742 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps); 08743 ao2_ref(queues, -1); 08744 ast_unload_realtime("queue_members"); 08745 return res; 08746 }
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 4175 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().
04176 { 04177 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value; 04178 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value; 04179 char max_penalty_str[20], min_penalty_str[20]; 04180 /* a relative change to the penalty could put it below 0 */ 04181 if (max_penalty < 0) 04182 max_penalty = 0; 04183 if (min_penalty < 0) 04184 min_penalty = 0; 04185 if (min_penalty > max_penalty) 04186 min_penalty = max_penalty; 04187 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty); 04188 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty); 04189 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str); 04190 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str); 04191 qe->max_penalty = max_penalty; 04192 qe->min_penalty = min_penalty; 04193 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time); 04194 qe->pr = AST_LIST_NEXT(qe->pr, list); 04195 }
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 4283 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().
04284 { 04285 int oldtalktime; 04286 04287 struct member *mem; 04288 struct call_queue *qtmp; 04289 struct ao2_iterator queue_iter; 04290 04291 if (shared_lastcall) { 04292 queue_iter = ao2_iterator_init(queues, 0); 04293 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 04294 ao2_lock(qtmp); 04295 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { 04296 time(&mem->lastcall); 04297 mem->calls++; 04298 mem->lastqueue = q; 04299 ao2_ref(mem, -1); 04300 } 04301 ao2_unlock(qtmp); 04302 queue_t_unref(qtmp, "Done with iterator"); 04303 } 04304 ao2_iterator_destroy(&queue_iter); 04305 } else { 04306 ao2_lock(q); 04307 time(&member->lastcall); 04308 member->calls++; 04309 member->lastqueue = q; 04310 ao2_unlock(q); 04311 } 04312 ao2_lock(q); 04313 q->callscompleted++; 04314 if (callcompletedinsl) 04315 q->callscompletedinsl++; 04316 /* Calculate talktime using the same exponential average as holdtime code*/ 04317 oldtalktime = q->talktime; 04318 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2; 04319 ao2_unlock(q); 04320 return 0; 04321 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 2471 of file app_queue.c.
References ast_strlen_zero(), ast_update_realtime(), member::rt_uniqueid, and SENTINEL.
Referenced by set_member_paused().
02472 { 02473 int ret = -1; 02474 02475 if (ast_strlen_zero(mem->rt_uniqueid)) 02476 return ret; 02477 02478 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) 02479 ret = 0; 02480 02481 return ret; 02482 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 2485 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().
02486 { 02487 struct ast_config *member_config = NULL; 02488 struct member *m; 02489 char *interface = NULL; 02490 struct ao2_iterator mem_iter; 02491 02492 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) { 02493 /* This queue doesn't have realtime members. If the queue still has any realtime 02494 * members in memory, they need to be removed. 02495 */ 02496 ao2_lock(q); 02497 mem_iter = ao2_iterator_init(q->members, 0); 02498 while ((m = ao2_iterator_next(&mem_iter))) { 02499 if (m->realtime) { 02500 member_remove_from_queue(q, m); 02501 } 02502 ao2_ref(m, -1); 02503 } 02504 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name); 02505 ao2_unlock(q); 02506 return; 02507 } 02508 02509 ao2_lock(q); 02510 02511 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 02512 mem_iter = ao2_iterator_init(q->members, 0); 02513 while ((m = ao2_iterator_next(&mem_iter))) { 02514 if (m->realtime) 02515 m->dead = 1; 02516 ao2_ref(m, -1); 02517 } 02518 ao2_iterator_destroy(&mem_iter); 02519 02520 while ((interface = ast_category_browse(member_config, interface))) { 02521 rt_handle_member_record(q, interface, 02522 ast_variable_retrieve(member_config, interface, "uniqueid"), 02523 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 02524 ast_variable_retrieve(member_config, interface, "penalty"), 02525 ast_variable_retrieve(member_config, interface, "paused"), 02526 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface)); 02527 } 02528 02529 /* Delete all realtime members that have been deleted in DB. */ 02530 mem_iter = ao2_iterator_init(q->members, 0); 02531 while ((m = ao2_iterator_next(&mem_iter))) { 02532 if (m->dead) { 02533 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 02534 member_remove_from_queue(q, m); 02535 } 02536 ao2_ref(m, -1); 02537 } 02538 ao2_iterator_destroy(&mem_iter); 02539 ao2_unlock(q); 02540 ast_config_destroy(member_config); 02541 }
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 1481 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().
01482 { 01483 m->status = status; 01484 01485 if (q->maskmemberstatus) 01486 return 0; 01487 01488 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 01489 "Queue: %s\r\n" 01490 "Location: %s\r\n" 01491 "MemberName: %s\r\n" 01492 "Membership: %s\r\n" 01493 "Penalty: %d\r\n" 01494 "CallsTaken: %d\r\n" 01495 "LastCall: %d\r\n" 01496 "Status: %d\r\n" 01497 "Paused: %d\r\n", 01498 q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static", 01499 m->penalty, m->calls, (int)m->lastcall, m->status, m->paused 01500 ); 01501 01502 return 0; 01503 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
UnPauseQueueMember application.
Definition at line 5865 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().
05866 { 05867 char *parse; 05868 AST_DECLARE_APP_ARGS(args, 05869 AST_APP_ARG(queuename); 05870 AST_APP_ARG(interface); 05871 AST_APP_ARG(options); 05872 AST_APP_ARG(reason); 05873 ); 05874 05875 if (ast_strlen_zero(data)) { 05876 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n"); 05877 return -1; 05878 } 05879 05880 parse = ast_strdupa(data); 05881 05882 AST_STANDARD_APP_ARGS(args, parse); 05883 05884 if (ast_strlen_zero(args.interface)) { 05885 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 05886 return -1; 05887 } 05888 05889 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) { 05890 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 05891 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 05892 return 0; 05893 } 05894 05895 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 05896 05897 return 0; 05898 }
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 2658 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().
02659 { 02660 int digitlen = strlen(qe->digits); 02661 02662 /* Prevent possible buffer overflow */ 02663 if (digitlen < sizeof(qe->digits) - 2) { 02664 qe->digits[digitlen] = digit; 02665 qe->digits[digitlen + 1] = '\0'; 02666 } else { 02667 qe->digits[0] = '\0'; 02668 return 0; 02669 } 02670 02671 /* If there's no context to goto, short-circuit */ 02672 if (ast_strlen_zero(qe->context)) 02673 return 0; 02674 02675 /* If the extension is bad, then reset the digits to blank */ 02676 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, 02677 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) { 02678 qe->digits[0] = '\0'; 02679 return 0; 02680 } 02681 02682 /* We have an exact match */ 02683 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 02684 qe->valid_digits = 1; 02685 /* Return 1 on a successful goto */ 02686 return 1; 02687 } 02688 02689 return 0; 02690 }
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 3046 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().
03047 { 03048 struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1); 03049 const char *tmp; 03050 03051 if (pbx_builtin_serialize_variables(chan, &buf)) { 03052 int i, j; 03053 03054 /* convert "\n" to "\nVariable: " */ 03055 strcpy(vars, "Variable: "); 03056 tmp = ast_str_buffer(buf); 03057 03058 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 03059 vars[j] = tmp[i]; 03060 03061 if (tmp[i + 1] == '\0') 03062 break; 03063 if (tmp[i] == '\n') { 03064 vars[j++] = '\r'; 03065 vars[j++] = '\n'; 03066 03067 ast_copy_string(&(vars[j]), "Variable: ", len - j); 03068 j += 9; 03069 } 03070 } 03071 if (j > len - 3) 03072 j = len - 3; 03073 vars[j++] = '\r'; 03074 vars[j++] = '\n'; 03075 vars[j] = '\0'; 03076 } else { 03077 /* there are no channel variables; leave it blank */ 03078 *vars = '\0'; 03079 } 03080 return vars; 03081 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 5387 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
05388 { 05389 /* Don't need to hold the lock while we setup the outgoing calls */ 05390 int retrywait = qe->parent->retry * 1000; 05391 05392 int res = ast_waitfordigit(qe->chan, retrywait); 05393 if (res > 0 && !valid_exit(qe, res)) 05394 res = 0; 05395 05396 return res; 05397 }
static struct callattempt* wait_for_answer | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing, | |||
int * | to, | |||
char * | digit, | |||
int | prebusies, | |||
int | caller_disconnect, | |||
int | forwardsallowed | |||
) | [static, read] |
Wait for a member to answer the call.
[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 3607 of file app_queue.c.
References ast_channel::_state, accountcode, 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, 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, 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().
03608 { 03609 const char *queue = qe->parent->name; 03610 struct callattempt *o, *start = NULL, *prev = NULL; 03611 int status; 03612 int numbusies = prebusies; 03613 int numnochan = 0; 03614 int stillgoing = 0; 03615 int orig = *to; 03616 struct ast_frame *f; 03617 struct callattempt *peer = NULL; 03618 struct ast_channel *winner; 03619 struct ast_channel *in = qe->chan; 03620 char on[80] = ""; 03621 char membername[80] = ""; 03622 long starttime = 0; 03623 long endtime = 0; 03624 #ifdef HAVE_EPOLL 03625 struct callattempt *epollo; 03626 #endif 03627 struct ast_party_connected_line connected_caller; 03628 char *inchan_name; 03629 struct timeval start_time_tv = ast_tvnow(); 03630 03631 ast_party_connected_line_init(&connected_caller); 03632 03633 ast_channel_lock(qe->chan); 03634 inchan_name = ast_strdupa(qe->chan->name); 03635 ast_channel_unlock(qe->chan); 03636 03637 starttime = (long) time(NULL); 03638 #ifdef HAVE_EPOLL 03639 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 03640 if (epollo->chan) 03641 ast_poll_channel_add(in, epollo->chan); 03642 } 03643 #endif 03644 03645 while ((*to = ast_remaining_ms(start_time_tv, orig)) && !peer) { 03646 int numlines, retry, pos = 1; 03647 struct ast_channel *watchers[AST_MAX_WATCHERS]; 03648 watchers[0] = in; 03649 start = NULL; 03650 03651 for (retry = 0; retry < 2; retry++) { 03652 numlines = 0; 03653 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 03654 if (o->stillgoing) { /* Keep track of important channels */ 03655 stillgoing = 1; 03656 if (o->chan) { 03657 if (pos < AST_MAX_WATCHERS) { 03658 watchers[pos++] = o->chan; 03659 } 03660 if (!start) 03661 start = o; 03662 else 03663 prev->call_next = o; 03664 prev = o; 03665 } 03666 } 03667 numlines++; 03668 } 03669 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 03670 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 03671 break; 03672 /* On "ringall" strategy we only move to the next penalty level 03673 when *all* ringing phones are done in the current penalty level */ 03674 ring_one(qe, outgoing, &numbusies); 03675 /* and retry... */ 03676 } 03677 if (pos == 1 /* not found */) { 03678 if (numlines == (numbusies + numnochan)) { 03679 ast_debug(1, "Everyone is busy at this time\n"); 03680 //if (in->cdr && in->_state != AST_STATE_UP) { 03681 // ast_cdr_busy(in->cdr); 03682 //} 03683 } else { 03684 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan); 03685 if (in->cdr && in->_state != AST_STATE_UP) { 03686 ast_cdr_failed(in->cdr); 03687 } 03688 } 03689 *to = 0; 03690 return NULL; 03691 } 03692 03693 /* Poll for events from both the incoming channel as well as any outgoing channels */ 03694 winner = ast_waitfor_n(watchers, pos, to); 03695 03696 /* Service all of the outgoing channels */ 03697 for (o = start; o; o = o->call_next) { 03698 /* We go with a fixed buffer here instead of using ast_strdupa. Using 03699 * ast_strdupa in a loop like this one can cause a stack overflow 03700 */ 03701 char ochan_name[AST_CHANNEL_NAME]; 03702 03703 if (o->chan) { 03704 ast_channel_lock(o->chan); 03705 ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); 03706 ast_channel_unlock(o->chan); 03707 } 03708 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 03709 if (!peer) { 03710 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 03711 if (!o->block_connected_update) { 03712 if (o->pending_connected_update) { 03713 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { 03714 ast_channel_update_connected_line(in, &o->connected, NULL); 03715 } 03716 } else if (!o->dial_callerid_absent) { 03717 ast_channel_lock(o->chan); 03718 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); 03719 ast_channel_unlock(o->chan); 03720 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; 03721 if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) { 03722 ast_channel_update_connected_line(in, &connected_caller, NULL); 03723 } 03724 ast_party_connected_line_free(&connected_caller); 03725 } 03726 } 03727 if (o->aoc_s_rate_list) { 03728 size_t encoded_size; 03729 struct ast_aoc_encoded *encoded; 03730 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) { 03731 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size); 03732 ast_aoc_destroy_encoded(encoded); 03733 } 03734 } 03735 peer = o; 03736 } 03737 } else if (o->chan && (o->chan == winner)) { 03738 03739 ast_copy_string(on, o->member->interface, sizeof(on)); 03740 ast_copy_string(membername, o->member->membername, sizeof(membername)); 03741 03742 /* Before processing channel, go ahead and check for forwarding */ 03743 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 03744 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); 03745 numnochan++; 03746 do_hang(o); 03747 winner = NULL; 03748 continue; 03749 } else if (!ast_strlen_zero(o->chan->call_forward)) { 03750 struct ast_channel *original = o->chan; 03751 char tmpchan[256]; 03752 char *stuff; 03753 char *tech; 03754 03755 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 03756 if ((stuff = strchr(tmpchan, '/'))) { 03757 *stuff++ = '\0'; 03758 tech = tmpchan; 03759 } else { 03760 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 03761 stuff = tmpchan; 03762 tech = "Local"; 03763 } 03764 if (!strcasecmp(tech, "Local")) { 03765 /* 03766 * Drop the connected line update block for local channels since 03767 * this is going to run dialplan and the user can change his 03768 * mind about what connected line information he wants to send. 03769 */ 03770 o->block_connected_update = 0; 03771 } 03772 03773 ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL); 03774 03775 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); 03776 /* Setup parameters */ 03777 o->chan = ast_request(tech, in->nativeformats, in, stuff, &status); 03778 if (!o->chan) { 03779 ast_log(LOG_NOTICE, 03780 "Forwarding failed to create channel to dial '%s/%s'\n", 03781 tech, stuff); 03782 o->stillgoing = 0; 03783 numnochan++; 03784 } else { 03785 ast_channel_lock_both(o->chan, original); 03786 ast_party_redirecting_copy(&o->chan->redirecting, &original->redirecting); 03787 ast_channel_unlock(o->chan); 03788 ast_channel_unlock(original); 03789 03790 ast_channel_lock_both(o->chan, in); 03791 ast_channel_inherit_variables(in, o->chan); 03792 ast_channel_datastore_inherit(in, o->chan); 03793 03794 if (o->pending_connected_update) { 03795 /* 03796 * Re-seed the callattempt's connected line information with 03797 * previously acquired connected line info from the queued 03798 * channel. The previously acquired connected line info could 03799 * have been set through the CONNECTED_LINE dialplan function. 03800 */ 03801 o->pending_connected_update = 0; 03802 ast_party_connected_line_copy(&o->connected, &in->connected); 03803 } 03804 03805 ast_string_field_set(o->chan, accountcode, in->accountcode); 03806 03807 if (!o->chan->redirecting.from.number.valid 03808 || ast_strlen_zero(o->chan->redirecting.from.number.str)) { 03809 /* 03810 * The call was not previously redirected so it is 03811 * now redirected from this number. 03812 */ 03813 ast_party_number_free(&o->chan->redirecting.from.number); 03814 ast_party_number_init(&o->chan->redirecting.from.number); 03815 o->chan->redirecting.from.number.valid = 1; 03816 o->chan->redirecting.from.number.str = 03817 ast_strdup(S_OR(in->macroexten, in->exten)); 03818 } 03819 03820 o->chan->dialed.transit_network_select = in->dialed.transit_network_select; 03821 03822 o->dial_callerid_absent = !o->chan->caller.id.number.valid 03823 || ast_strlen_zero(o->chan->caller.id.number.str); 03824 ast_connected_line_copy_from_caller(&o->chan->connected, &in->caller); 03825 03826 ast_channel_unlock(in); 03827 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL 03828 && !o->block_connected_update) { 03829 struct ast_party_redirecting redirecting; 03830 03831 /* 03832 * Redirecting updates to the caller make sense only on single 03833 * call at a time strategies. 03834 * 03835 * We must unlock o->chan before calling 03836 * ast_channel_redirecting_macro, because we put o->chan into 03837 * autoservice there. That is pretty much a guaranteed 03838 * deadlock. This is why the handling of o->chan's lock may 03839 * seem a bit unusual here. 03840 */ 03841 ast_party_redirecting_init(&redirecting); 03842 ast_party_redirecting_copy(&redirecting, &o->chan->redirecting); 03843 ast_channel_unlock(o->chan); 03844 if (ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0)) { 03845 ast_channel_update_redirecting(in, &redirecting, NULL); 03846 } 03847 ast_party_redirecting_free(&redirecting); 03848 } else { 03849 ast_channel_unlock(o->chan); 03850 } 03851 03852 if (ast_call(o->chan, stuff, 0)) { 03853 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", 03854 tech, stuff); 03855 do_hang(o); 03856 numnochan++; 03857 } 03858 } 03859 /* Hangup the original channel now, in case we needed it */ 03860 ast_hangup(winner); 03861 continue; 03862 } 03863 f = ast_read(winner); 03864 if (f) { 03865 if (f->frametype == AST_FRAME_CONTROL) { 03866 switch (f->subclass.integer) { 03867 case AST_CONTROL_ANSWER: 03868 /* This is our guy if someone answered. */ 03869 if (!peer) { 03870 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 03871 if (!o->block_connected_update) { 03872 if (o->pending_connected_update) { 03873 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { 03874 ast_channel_update_connected_line(in, &o->connected, NULL); 03875 } 03876 } else if (!o->dial_callerid_absent) { 03877 ast_channel_lock(o->chan); 03878 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); 03879 ast_channel_unlock(o->chan); 03880 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; 03881 if (ast_channel_connected_line_macro(o->chan, in, &connected_caller, 1, 0)) { 03882 ast_channel_update_connected_line(in, &connected_caller, NULL); 03883 } 03884 ast_party_connected_line_free(&connected_caller); 03885 } 03886 } 03887 if (o->aoc_s_rate_list) { 03888 size_t encoded_size; 03889 struct ast_aoc_encoded *encoded; 03890 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) { 03891 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size); 03892 ast_aoc_destroy_encoded(encoded); 03893 } 03894 } 03895 peer = o; 03896 } 03897 break; 03898 case AST_CONTROL_BUSY: 03899 ast_verb(3, "%s is busy\n", ochan_name); 03900 //if (in->cdr) 03901 // ast_cdr_busy(in->cdr); 03902 do_hang(o); 03903 endtime = (long) time(NULL); 03904 endtime -= starttime; 03905 rna(endtime * 1000, qe, on, membername, 0); 03906 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03907 if (qe->parent->timeoutrestart) { 03908 start_time_tv = ast_tvnow(); 03909 } 03910 /* Have enough time for a queue member to answer? */ 03911 if (ast_remaining_ms(start_time_tv, orig) > 500) { 03912 ring_one(qe, outgoing, &numbusies); 03913 starttime = (long) time(NULL); 03914 } 03915 } 03916 numbusies++; 03917 break; 03918 case AST_CONTROL_CONGESTION: 03919 ast_verb(3, "%s is circuit-busy\n", ochan_name); 03920 if (in->cdr) 03921 ast_cdr_failed(in->cdr); 03922 endtime = (long) time(NULL); 03923 endtime -= starttime; 03924 rna(endtime * 1000, qe, on, membername, 0); 03925 do_hang(o); 03926 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03927 if (qe->parent->timeoutrestart) { 03928 start_time_tv = ast_tvnow(); 03929 } 03930 if (ast_remaining_ms(start_time_tv, orig) > 500) { 03931 ring_one(qe, outgoing, &numbusies); 03932 starttime = (long) time(NULL); 03933 } 03934 } 03935 numbusies++; 03936 break; 03937 case AST_CONTROL_RINGING: 03938 ast_verb(3, "%s is ringing\n", ochan_name); 03939 03940 /* Start ring indication when the channel is ringing, if specified */ 03941 if (qe->ring_when_ringing) { 03942 ast_moh_stop(qe->chan); 03943 ast_indicate(qe->chan, AST_CONTROL_RINGING); 03944 } 03945 break; 03946 case AST_CONTROL_OFFHOOK: 03947 /* Ignore going off hook */ 03948 break; 03949 case AST_CONTROL_CONNECTED_LINE: 03950 if (o->block_connected_update) { 03951 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); 03952 break; 03953 } 03954 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 03955 struct ast_party_connected_line connected; 03956 03957 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name); 03958 ast_party_connected_line_set_init(&connected, &o->connected); 03959 ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); 03960 ast_party_connected_line_set(&o->connected, &connected, NULL); 03961 ast_party_connected_line_free(&connected); 03962 o->pending_connected_update = 1; 03963 break; 03964 } 03965 03966 /* 03967 * Prevent using the CallerID from the outgoing channel since we 03968 * got a connected line update from it. 03969 */ 03970 o->dial_callerid_absent = 1; 03971 03972 if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) { 03973 ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); 03974 } 03975 break; 03976 case AST_CONTROL_AOC: 03977 { 03978 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan); 03979 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) { 03980 ast_aoc_destroy_decoded(o->aoc_s_rate_list); 03981 o->aoc_s_rate_list = decoded; 03982 } else { 03983 ast_aoc_destroy_decoded(decoded); 03984 } 03985 } 03986 break; 03987 case AST_CONTROL_REDIRECTING: 03988 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 03989 /* 03990 * Redirecting updates to the caller make sense only on single 03991 * call at a time strategies. 03992 */ 03993 break; 03994 } 03995 if (o->block_connected_update) { 03996 ast_verb(3, "Redirecting update to %s prevented\n", 03997 inchan_name); 03998 break; 03999 } 04000 ast_verb(3, "%s redirecting info has changed, passing it to %s\n", 04001 ochan_name, inchan_name); 04002 if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) { 04003 ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); 04004 } 04005 break; 04006 default: 04007 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer); 04008 break; 04009 } 04010 } 04011 ast_frfree(f); 04012 } else { /* ast_read() returned NULL */ 04013 endtime = (long) time(NULL) - starttime; 04014 rna(endtime * 1000, qe, on, membername, 1); 04015 do_hang(o); 04016 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 04017 if (qe->parent->timeoutrestart) { 04018 start_time_tv = ast_tvnow(); 04019 } 04020 if (ast_remaining_ms(start_time_tv, orig) > 500) { 04021 ring_one(qe, outgoing, &numbusies); 04022 starttime = (long) time(NULL); 04023 } 04024 } 04025 } 04026 } 04027 } 04028 04029 /* If we received an event from the caller, deal with it. */ 04030 if (winner == in) { 04031 f = ast_read(in); 04032 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) { 04033 /* Got hung up */ 04034 *to = -1; 04035 if (f) { 04036 if (f->data.uint32) { 04037 in->hangupcause = f->data.uint32; 04038 } 04039 ast_frfree(f); 04040 } 04041 return NULL; 04042 } 04043 04044 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) { 04045 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer); 04046 *to = 0; 04047 ast_frfree(f); 04048 if (in->cdr && in->_state != AST_STATE_UP) { 04049 ast_cdr_noanswer(in->cdr); 04050 } 04051 return NULL; 04052 } 04053 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) { 04054 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer); 04055 *to = 0; 04056 *digit = f->subclass.integer; 04057 ast_frfree(f); 04058 if (in->cdr && in->_state != AST_STATE_UP) { 04059 ast_cdr_noanswer(in->cdr); 04060 } 04061 return NULL; 04062 } 04063 04064 /* Send the frame from the in channel to all outgoing channels. */ 04065 for (o = start; o; o = o->call_next) { 04066 if (!o->stillgoing || !o->chan) { 04067 /* This outgoing channel has died so don't send the frame to it. */ 04068 continue; 04069 } 04070 switch (f->frametype) { 04071 case AST_FRAME_CONTROL: 04072 switch (f->subclass.integer) { 04073 case AST_CONTROL_CONNECTED_LINE: 04074 if (ast_channel_connected_line_macro(in, o->chan, f, 0, 1)) { 04075 ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen); 04076 } 04077 break; 04078 case AST_CONTROL_REDIRECTING: 04079 if (ast_channel_redirecting_macro(in, o->chan, f, 0, 1)) { 04080 ast_indicate_data(o->chan, f->subclass.integer, f->data.ptr, f->datalen); 04081 } 04082 break; 04083 default: 04084 /* We are not going to do anything with this frame. */ 04085 goto skip_frame; 04086 } 04087 break; 04088 default: 04089 /* We are not going to do anything with this frame. */ 04090 goto skip_frame; 04091 } 04092 } 04093 skip_frame:; 04094 04095 ast_frfree(f); 04096 } 04097 } 04098 04099 if (!*to) { 04100 for (o = start; o; o = o->call_next) { 04101 rna(orig, qe, o->interface, o->member->membername, 1); 04102 } 04103 } 04104 04105 if (in->cdr 04106 && in->_state != AST_STATE_UP 04107 && (!*to || ast_check_hangup(in))) { 04108 ast_cdr_noanswer(in->cdr); 04109 } 04110 04111 #ifdef HAVE_EPOLL 04112 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 04113 if (epollo->chan) 04114 ast_poll_channel_del(in, epollo->chan); 04115 } 04116 #endif 04117 04118 return peer; 04119 }
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 4207 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().
04208 { 04209 int res = 0; 04210 04211 /* This is the holding pen for callers 2 through maxlen */ 04212 for (;;) { 04213 04214 if (is_our_turn(qe)) 04215 break; 04216 04217 /* If we have timed out, break out */ 04218 if (qe->expire && (time(NULL) >= qe->expire)) { 04219 *reason = QUEUE_TIMEOUT; 04220 break; 04221 } 04222 04223 if (qe->parent->leavewhenempty) { 04224 int status = 0; 04225 04226 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) { 04227 *reason = QUEUE_LEAVEEMPTY; 04228 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 04229 leave_queue(qe); 04230 break; 04231 } 04232 } 04233 04234 /* Make a position announcement, if enabled */ 04235 if (qe->parent->announcefrequency && 04236 (res = say_position(qe,ringing))) 04237 break; 04238 04239 /* If we have timed out, break out */ 04240 if (qe->expire && (time(NULL) >= qe->expire)) { 04241 *reason = QUEUE_TIMEOUT; 04242 break; 04243 } 04244 04245 /* Make a periodic announcement, if enabled */ 04246 if (qe->parent->periodicannouncefrequency && 04247 (res = say_periodic_announcement(qe,ringing))) 04248 break; 04249 04250 /* see if we need to move to the next penalty level for this queue */ 04251 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) { 04252 update_qe_rule(qe); 04253 } 04254 04255 /* If we have timed out, break out */ 04256 if (qe->expire && (time(NULL) >= qe->expire)) { 04257 *reason = QUEUE_TIMEOUT; 04258 break; 04259 } 04260 04261 /* Wait a second before checking again */ 04262 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 04263 if (res > 0 && !valid_exit(qe, res)) 04264 res = 0; 04265 else 04266 break; 04267 } 04268 04269 /* If we have timed out, break out */ 04270 if (qe->expire && (time(NULL) >= qe->expire)) { 04271 *reason = QUEUE_TIMEOUT; 04272 break; 04273 } 04274 } 04275 04276 return res; 04277 }
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 7368 of file app_queue.c.
Referenced by complete_queue().
07368 { 07369 int list_len, word_len = strlen(word); 07370 const char *find, *end_find, *end_list; 07371 07372 /* strip whitespace from front */ 07373 while (isspace(*list)) { 07374 list++; 07375 } 07376 07377 while ((find = strstr(list, word))) { 07378 /* beginning of find starts inside another word? */ 07379 if (find != list && *(find - 1) != ' ') { 07380 list = find; 07381 /* strip word from front */ 07382 while (!isspace(*list) && *list != '\0') { 07383 list++; 07384 } 07385 /* strip whitespace from front */ 07386 while (isspace(*list)) { 07387 list++; 07388 } 07389 continue; 07390 } 07391 07392 /* end of find ends inside another word or at very end of list? */ 07393 list_len = strlen(list); 07394 end_find = find + word_len; 07395 end_list = list + list_len; 07396 if (end_find == end_list || *end_find != ' ') { 07397 list = find; 07398 /* strip word from front */ 07399 while (!isspace(*list) && *list != '\0') { 07400 list++; 07401 } 07402 /* strip whitespace from front */ 07403 while (isspace(*list)) { 07404 list++; 07405 } 07406 continue; 07407 } 07408 07409 /* terminating conditions satisfied, word at beginning or separated by ' ' */ 07410 return 1; 07411 } 07412 07413 return 0; 07414 }
char* app = "Queue" [static] |
Definition at line 901 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 903 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 907 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 911 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 905 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 909 of file app_queue.c.
int autofill_default = 1 [static] |
queues.conf [general] option
Definition at line 923 of file app_queue.c.
struct autopause autopausesmodes[] [static] |
Referenced by autopause2int().
struct ast_cli_entry cli_queue[] [static] |
Definition at line 8412 of file app_queue.c.
struct ast_event_sub* device_state_sub [static] |
Subscription to device state change events.
Definition at line 932 of file app_queue.c.
struct ast_taskprocessor* devicestate_tps [static] |
Definition at line 885 of file app_queue.c.
enum queue_result id |
Definition at line 949 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 926 of file app_queue.c.
const char* const pm_family = "Queue/PersistentMembers" [static] |
Persistent Members astdb family.
Definition at line 914 of file app_queue.c.
const char qpm_cmd_usage[] [static] |
"Usage: queue pause member <channel> in <queue> reason <reason>\n"
Definition at line 8403 of file app_queue.c.
const char qsmp_cmd_usage[] [static] |
"Usage: queue set member penalty <channel> from <queue> <penalty>\n"
Definition at line 8409 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 8689 of file app_queue.c.
int queue_persistent_members = 0 [static] |
queues.conf [general] option
Definition at line 917 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 4459 of file app_queue.c.
struct ast_custom_function queueexists_function [static] |
{ .name = "QUEUE_EXISTS", .read = queue_function_exists, }
Definition at line 6726 of file app_queue.c.
struct ast_custom_function queuemembercount_dep [static] |
{ .name = "QUEUE_MEMBER_COUNT", .read = queue_function_qac_dep, }
Definition at line 6741 of file app_queue.c.
struct ast_custom_function queuemembercount_function [static] |
{ .name = "QUEUE_MEMBER", .read = queue_function_qac, }
Definition at line 6736 of file app_queue.c.
struct ast_custom_function queuememberlist_function [static] |
{ .name = "QUEUE_MEMBER_LIST", .read = queue_function_queuememberlist, }
Definition at line 6751 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 6756 of file app_queue.c.
struct ao2_container* queues [static] |
Definition at line 1189 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_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 8684 of file app_queue.c.
struct ast_custom_function queuevar_function [static] |
{ .name = "QUEUE_VARIABLES", .read = queue_function_var, }
Definition at line 6731 of file app_queue.c.
struct ast_custom_function queuewaitingcount_function [static] |
{ .name = "QUEUE_WAITING_COUNT", .read = queue_function_queuewaitingcount, }
Definition at line 6746 of file app_queue.c.
const char qum_cmd_usage[] [static] |
"Usage: queue unpause member <channel> in <queue> reason <reason>\n"
Definition at line 8406 of file app_queue.c.
int shared_lastcall = 1 [static] |
queues.conf [general] option
Definition at line 929 of file app_queue.c.
struct strategy strategies[] [static] |
Referenced by int2strat(), and strat2int().
char* text |
Definition at line 950 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 935 of file app_queue.c.
Referenced by login_exec().
int use_weight = 0 [static] |
queues.conf per-queue weight option
Definition at line 920 of file app_queue.c.