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