#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 | rule_lists |
struct | statechange |
struct | strategy |
Defines | |
#define | ANNOUNCEHOLDTIME_ALWAYS 1 |
#define | ANNOUNCEHOLDTIME_ONCE 2 |
#define | ANNOUNCEPOSITION_LIMIT 4 |
#define | ANNOUNCEPOSITION_MORE_THAN 3 |
#define | ANNOUNCEPOSITION_NO 2 |
#define | ANNOUNCEPOSITION_YES 1 |
#define | AST_MAX_WATCHERS 256 |
#define | 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 | PM_MAX_LEN 8192 |
#define | QUEUE_EVENT_VARIABLES 3 |
#define | queue_t_ref(a, b) queue_ref(a) |
#define | queue_t_unref(a, b) queue_unref(a) |
#define | queues_t_link(c, q, tag) ao2_t_link(c,q,tag) |
#define | queues_t_unlink(c, q, tag) ao2_t_unlink(c,q,tag) |
#define | RECHECK 1 |
#define | RES_EXISTS (-1) |
#define | RES_NOSUCHQUEUE (-3) |
#define | RES_NOT_DYNAMIC (-4) |
#define | RES_OKAY 0 |
#define | RES_OUTOFMEMORY (-2) |
Enumerations | |
enum | { QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM } |
enum | { 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 void | __reg_module (void) |
static void | __unreg_module (void) |
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 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 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) |
static char * | complete_queue_add_member (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_pause_member (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_remove_member (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_rule_show (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_show (const char *line, const char *word, int pos, int state) |
static int | compress_char (const char c) |
static void | copy_rules (struct queue_ent *qe, const char *rulename) |
Copy rule from global list into specified queue. | |
static struct member * | create_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface) |
allocate space for new queue member and set fields based on parameters passed | |
static void | destroy_queue (void *obj) |
Free queue's member list then its string fields. | |
static void | device_state_cb (const struct ast_event *event, void *unused) |
static void | do_hang (struct callattempt *o) |
common hangup actions | |
static void | do_print (struct mansession *s, int fd, const char *str) |
direct ouput to manager or cli with proper terminator | |
static void | dump_queue_members (struct call_queue *pm_queue) |
Dump all members in a specific queue to the database. | |
static void | end_bridge_callback (void *data) |
static void | end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) |
static int | extension_state_cb (char *context, char *exten, enum ast_extension_states state, void *data) |
static int | extensionstate2devicestate (int state) |
Helper function which converts from extension state to device state values. | |
static struct callattempt * | find_best (struct callattempt *outgoing) |
find the entry with the best metric, or NULL | |
static struct call_queue * | find_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config) |
Reload a single queue via realtime. | |
static void | free_members (struct call_queue *q, int all) |
Iterate through queue's member list and delete them. | |
static int | get_member_penalty (char *queuename, char *interface) |
static int | get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions) |
Check if members are available. | |
static int | get_queue_member_status (struct member *cur) |
Return the current state of a member. | |
static char * | handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_reset (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static int | handle_statechange (void *datap) |
set a member's status based on device state of that member's interface | |
static void | hangupcalls (struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere) |
Hang up a list of outgoing calls. | |
static void | init_queue (struct call_queue *q) |
Initialize Queue default values. | |
static void | insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos) |
Insert the 'new' entry after the 'prev' entry of queue 'q'. | |
static int | insert_penaltychange (const char *list_name, const char *content, const int linenum) |
Change queue penalty by adding rule. | |
static const char * | int2strat (int strategy) |
static struct member * | interface_exists (struct call_queue *q, const char *interface) |
static int | is_our_turn (struct queue_ent *qe) |
Check if we should start attempting to call queue members. | |
static int | join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, int position) |
static int | kill_dead_members (void *obj, void *arg, int flags) |
static int | kill_dead_queues (void *obj, void *arg, int flags) |
static void | leave_queue (struct queue_ent *qe) |
Caller leaving queue. | |
static int | load_module (void) |
static struct call_queue * | load_realtime_queue (const char *queuename) |
static int | manager_add_queue_member (struct mansession *s, const struct message *m) |
static int | manager_pause_queue_member (struct mansession *s, const struct message *m) |
static int | manager_queue_log_custom (struct mansession *s, const struct message *m) |
static int | manager_queue_member_penalty (struct mansession *s, const struct message *m) |
static int | manager_queue_reload (struct mansession *s, const struct message *m) |
static int | manager_queue_reset (struct mansession *s, const struct message *m) |
static int | manager_queue_rule_show (struct mansession *s, const struct message *m) |
static int | manager_queues_show (struct mansession *s, const struct message *m) |
static int | manager_queues_status (struct mansession *s, const struct message *m) |
Queue status info via AMI. | |
static int | manager_queues_summary (struct mansession *s, const struct message *m) |
Summary of queue info via the AMI. | |
static int | manager_remove_queue_member (struct mansession *s, const struct message *m) |
static int | mark_dead_and_unfound (void *obj, void *arg, int flags) |
static int | mark_member_dead (void *obj, void *arg, int flags) |
static int | member_cmp_fn (void *obj1, void *obj2, int flags) |
static int | member_hash_fn (const void *obj, const int flags) |
static int | num_available_members (struct call_queue *q) |
Get the number of members available to accept a call. | |
static 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_exec (struct ast_channel *chan, const char *data) |
The starting point for all queue calls. | |
static int | queue_function_exists (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Check if a given queue exists. | |
static int | queue_function_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. | |
static int | queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value) |
Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. | |
static int | queue_function_qac (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Get number either busy / free / ready or total members of a specific queue. | |
static int | queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Get the total number of members in a specific queue (Deprecated). | |
static int | queue_function_queuememberlist (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue. | |
static int | queue_function_queuewaitingcount (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue. | |
static int | queue_function_var (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
create interface var with all queue details. | |
static int | queue_hash_cb (const void *obj, const int flags) |
static 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 | 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 peroid has elapsed. | |
static int | say_position (struct queue_ent *qe, int ringing) |
static void | send_agent_complete (const struct queue_ent *qe, const char *queuename, const struct ast_channel *peer, const struct member *member, time_t callstart, char *vars, size_t vars_len, enum agent_complete_reason rsn) |
Send out AMI message with member call completion status information. | |
static int | set_member_paused (const char *queuename, const char *interface, const char *reason, int paused) |
static int | set_member_penalty (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 update_connectedline) |
Wait for a member to answer the call. | |
static int | wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason) |
The waiting areas for callers who are not actively calling members. | |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", } |
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 struct ast_module_info * | ast_module_info = &__mod_info |
static int | autofill_default = 1 |
queues.conf [general] option | |
static struct autopause | autopausesmodes [] |
static struct ast_cli_entry | cli_queue [] |
static struct ast_event_sub * | device_state_sub |
Subscription to device state change events. | |
static struct ast_taskprocessor * | devicestate_tps |
static int | montype_default = 0 |
queues.conf [general] option | |
static const char *const | pm_family = "Queue/PersistentMembers" |
Persistent Members astdb family. | |
static const char | qpm_cmd_usage [] |
static const char | qsmp_cmd_usage [] |
static struct ast_data_entry | queue_data_providers [] |
static int | queue_persistent_members = 0 |
queues.conf [general] option | |
struct { | |
enum queue_result id | |
char * text | |
} | queue_results [] |
static struct ast_datastore_info | queue_transfer_info |
a datastore used to help correctly log attended transfers of queue callers | |
static struct ast_custom_function | queueexists_function |
static struct ast_custom_function | queuemembercount_dep |
static struct ast_custom_function | queuemembercount_function |
static struct ast_custom_function | queuememberlist_function |
static struct ast_custom_function | queuememberpenalty_function |
static struct ao2_container * | queues |
static struct ast_data_handler | queues_data_provider |
static struct ast_custom_function | queuevar_function |
static struct ast_custom_function | queuewaitingcount_function |
static const char | qum_cmd_usage [] |
static int | shared_lastcall = 1 |
queues.conf [general] option | |
static struct strategy | strategies [] |
static int | update_cdr = 0 |
queues.conf [general] option | |
static int | use_weight = 0 |
queues.conf per-queue weight option |
These features added by David C. Troy <dave@toad.net>:
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 |
#define ANNOUNCEHOLDTIME_ONCE 2 |
#define ANNOUNCEPOSITION_LIMIT 4 |
We not announce position more than <limit>
Definition at line 916 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 915 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 914 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 913 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 3249 of file app_queue.c.
#define DATA_EXPORT_CALL_QUEUE | ( | MEMBER | ) |
Definition at line 7805 of file app_queue.c.
#define DATA_EXPORT_MEMBER | ( | MEMBER | ) |
Definition at line 7870 of file app_queue.c.
#define DATA_EXPORT_QUEUE_ENT | ( | MEMBER | ) |
Definition at line 7884 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 729 of file app_queue.c.
Referenced by init_queue().
#define DEFAULT_RETRY 5 |
#define DEFAULT_TIMEOUT 15 |
#define MAX_PERIODIC_ANNOUNCEMENTS 10 |
The maximum periodic announcements we can have
Definition at line 728 of file app_queue.c.
Referenced by destroy_queue(), init_queue(), and queue_set_param().
#define MAX_QUEUE_BUCKETS 53 |
#define PM_MAX_LEN 8192 |
Definition at line 754 of file app_queue.c.
Referenced by dump_queue_members(), and reload_queue_members().
#define QUEUE_EVENT_VARIABLES 3 |
Definition at line 902 of file app_queue.c.
Referenced by queue_set_param(), ring_entry(), rna(), and send_agent_complete().
#define queue_t_ref | ( | a, | |||
b | ) | queue_ref(a) |
#define queue_t_unref | ( | a, | |||
b | ) | queue_unref(a) |
Definition at line 1115 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(), leave_queue(), manager_queues_status(), manager_queues_summary(), queue_function_exists(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().
#define queues_t_link | ( | c, | |||
q, | |||||
tag | ) | ao2_t_link(c,q,tag) |
Definition at line 1116 of file app_queue.c.
#define queues_t_unlink | ( | c, | |||
q, | |||||
tag | ) | ao2_t_unlink(c,q,tag) |
Definition at line 1117 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 727 of file app_queue.c.
#define RES_EXISTS (-1) |
Entry already exists
Definition at line 734 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 736 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 737 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 733 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 735 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 |
Definition at line 676 of file app_queue.c.
00676 { 00677 QUEUE_STRATEGY_RINGALL = 0, 00678 QUEUE_STRATEGY_LEASTRECENT, 00679 QUEUE_STRATEGY_FEWESTCALLS, 00680 QUEUE_STRATEGY_RANDOM, 00681 QUEUE_STRATEGY_RRMEMORY, 00682 QUEUE_STRATEGY_LINEAR, 00683 QUEUE_STRATEGY_WRANDOM 00684 };
anonymous enum |
Definition at line 686 of file app_queue.c.
00686 { 00687 QUEUE_AUTOPAUSE_OFF = 0, 00688 QUEUE_AUTOPAUSE_ON, 00689 QUEUE_AUTOPAUSE_ALL 00690 };
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 888 of file app_queue.c.
00888 { 00889 QUEUE_EMPTY_PENALTY = (1 << 0), 00890 QUEUE_EMPTY_PAUSED = (1 << 1), 00891 QUEUE_EMPTY_INUSE = (1 << 2), 00892 QUEUE_EMPTY_RINGING = (1 << 3), 00893 QUEUE_EMPTY_UNAVAILABLE = (1 << 4), 00894 QUEUE_EMPTY_INVALID = (1 << 5), 00895 QUEUE_EMPTY_UNKNOWN = (1 << 6), 00896 QUEUE_EMPTY_WRAPUP = (1 << 7), 00897 };
enum queue_reload_mask |
Definition at line 692 of file app_queue.c.
00692 { 00693 QUEUE_RELOAD_PARAMETERS = (1 << 0), 00694 QUEUE_RELOAD_MEMBER = (1 << 1), 00695 QUEUE_RELOAD_RULES = (1 << 2), 00696 QUEUE_RESET_STATS = (1 << 3), 00697 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL | |
QUEUE_CONTINUE |
Definition at line 777 of file app_queue.c.
00777 { 00778 QUEUE_UNKNOWN = 0, 00779 QUEUE_TIMEOUT = 1, 00780 QUEUE_JOINEMPTY = 2, 00781 QUEUE_LEAVEEMPTY = 3, 00782 QUEUE_JOINUNAVAIL = 4, 00783 QUEUE_LEAVEUNAVAIL = 5, 00784 QUEUE_FULL = 6, 00785 QUEUE_CONTINUE = 7, 00786 };
Definition at line 802 of file app_queue.c.
00802 { 00803 TIMEOUT_PRIORITY_APP, 00804 TIMEOUT_PRIORITY_CONF, 00805 };
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 6717 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, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_t_unref, member::realtime, call_queue::realtime, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
06718 { 06719 struct call_queue *q; 06720 struct ast_str *out = ast_str_alloca(240); 06721 int found = 0; 06722 time_t now = time(NULL); 06723 struct ao2_iterator queue_iter; 06724 struct ao2_iterator mem_iter; 06725 06726 if (argc != 2 && argc != 3) 06727 return CLI_SHOWUSAGE; 06728 06729 if (argc == 3) { /* specific queue */ 06730 if ((q = load_realtime_queue(argv[2]))) { 06731 queue_t_unref(q, "Done with temporary pointer"); 06732 } 06733 } else if (ast_check_realtime("queues")) { 06734 /* This block is to find any queues which are defined in realtime but 06735 * which have not yet been added to the in-core container 06736 */ 06737 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 06738 char *queuename; 06739 if (cfg) { 06740 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 06741 if ((q = load_realtime_queue(queuename))) { 06742 queue_t_unref(q, "Done with temporary pointer"); 06743 } 06744 } 06745 ast_config_destroy(cfg); 06746 } 06747 } 06748 06749 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK); 06750 ao2_lock(queues); 06751 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06752 float sl; 06753 struct call_queue *realtime_queue = NULL; 06754 06755 ao2_lock(q); 06756 /* This check is to make sure we don't print information for realtime 06757 * queues which have been deleted from realtime but which have not yet 06758 * been deleted from the in-core container 06759 */ 06760 if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) { 06761 ao2_unlock(q); 06762 queue_t_unref(q, "Done with iterator"); 06763 continue; 06764 } else if (q->realtime) { 06765 queue_t_unref(realtime_queue, "Queue is already in memory"); 06766 } 06767 if (argc == 3 && strcasecmp(q->name, argv[2])) { 06768 ao2_unlock(q); 06769 queue_t_unref(q, "Done with iterator"); 06770 continue; 06771 } 06772 found = 1; 06773 06774 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count); 06775 if (q->maxlen) 06776 ast_str_append(&out, 0, "%d", q->maxlen); 06777 else 06778 ast_str_append(&out, 0, "unlimited"); 06779 sl = 0; 06780 if (q->callscompleted > 0) 06781 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 06782 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds", 06783 int2strat(q->strategy), q->holdtime, q->talktime, q->weight, 06784 q->callscompleted, q->callsabandoned,sl,q->servicelevel); 06785 do_print(s, fd, ast_str_buffer(out)); 06786 if (!ao2_container_count(q->members)) 06787 do_print(s, fd, " No Members"); 06788 else { 06789 struct member *mem; 06790 06791 do_print(s, fd, " Members: "); 06792 mem_iter = ao2_iterator_init(q->members, 0); 06793 while ((mem = ao2_iterator_next(&mem_iter))) { 06794 ast_str_set(&out, 0, " %s", mem->membername); 06795 if (strcasecmp(mem->membername, mem->interface)) { 06796 ast_str_append(&out, 0, " (%s)", mem->interface); 06797 } 06798 if (mem->penalty) 06799 ast_str_append(&out, 0, " with penalty %d", mem->penalty); 06800 ast_str_append(&out, 0, "%s%s%s (%s)", 06801 mem->dynamic ? " (dynamic)" : "", 06802 mem->realtime ? " (realtime)" : "", 06803 mem->paused ? " (paused)" : "", 06804 ast_devstate2str(mem->status)); 06805 if (mem->calls) 06806 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)", 06807 mem->calls, (long) (time(NULL) - mem->lastcall)); 06808 else 06809 ast_str_append(&out, 0, " has taken no calls yet"); 06810 do_print(s, fd, ast_str_buffer(out)); 06811 ao2_ref(mem, -1); 06812 } 06813 ao2_iterator_destroy(&mem_iter); 06814 } 06815 if (!q->head) 06816 do_print(s, fd, " No Callers"); 06817 else { 06818 struct queue_ent *qe; 06819 int pos = 1; 06820 06821 do_print(s, fd, " Callers: "); 06822 for (qe = q->head; qe; qe = qe->next) { 06823 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)", 06824 pos++, qe->chan->name, (long) (now - qe->start) / 60, 06825 (long) (now - qe->start) % 60, qe->prio); 06826 do_print(s, fd, ast_str_buffer(out)); 06827 } 06828 } 06829 do_print(s, fd, ""); /* blank line between entries */ 06830 ao2_unlock(q); 06831 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */ 06832 } 06833 ao2_iterator_destroy(&queue_iter); 06834 ao2_unlock(queues); 06835 if (!found) { 06836 if (argc == 3) 06837 ast_str_set(&out, 0, "No such queue: %s.", argv[2]); 06838 else 06839 ast_str_set(&out, 0, "No queues."); 06840 do_print(s, fd, ast_str_buffer(out)); 06841 } 06842 return CLI_SUCCESS; 06843 }
static void __reg_module | ( | void | ) | [static] |
Definition at line 8205 of file app_queue.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 8205 of file app_queue.c.
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 5037 of file app_queue.c.
References ao2_link, ao2_lock, ao2_ref, ao2_unlock, member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), manager_event, call_queue::membercount, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, 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().
05038 { 05039 struct call_queue *q; 05040 struct member *new_member, *old_member; 05041 int res = RES_NOSUCHQUEUE; 05042 05043 /*! \note Ensure the appropriate realtime queue is loaded. Note that this 05044 * short-circuits if the queue is already in memory. */ 05045 if (!(q = load_realtime_queue(queuename))) 05046 return res; 05047 05048 ao2_lock(queues); 05049 05050 ao2_lock(q); 05051 if ((old_member = interface_exists(q, interface)) == NULL) { 05052 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) { 05053 new_member->dynamic = 1; 05054 ao2_link(q->members, new_member); 05055 q->membercount++; 05056 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 05057 "Queue: %s\r\n" 05058 "Location: %s\r\n" 05059 "MemberName: %s\r\n" 05060 "Membership: %s\r\n" 05061 "Penalty: %d\r\n" 05062 "CallsTaken: %d\r\n" 05063 "LastCall: %d\r\n" 05064 "Status: %d\r\n" 05065 "Paused: %d\r\n", 05066 q->name, new_member->interface, new_member->membername, 05067 "dynamic", 05068 new_member->penalty, new_member->calls, (int) new_member->lastcall, 05069 new_member->status, new_member->paused); 05070 05071 ao2_ref(new_member, -1); 05072 new_member = NULL; 05073 05074 if (dump) 05075 dump_queue_members(q); 05076 05077 res = RES_OKAY; 05078 } else { 05079 res = RES_OUTOFMEMORY; 05080 } 05081 } else { 05082 ao2_ref(old_member, -1); 05083 res = RES_EXISTS; 05084 } 05085 ao2_unlock(q); 05086 ao2_unlock(queues); 05087 queue_t_unref(q, "Expiring temporary reference"); 05088 05089 return res; 05090 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static] |
Definition at line 2008 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().
02009 { 02010 struct call_queue *q; 02011 02012 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) { 02013 if (ast_string_field_init(q, 64)) { 02014 queue_t_unref(q, "String field allocation failed"); 02015 return NULL; 02016 } 02017 ast_string_field_set(q, name, queuename); 02018 } 02019 return q; 02020 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
AddQueueMember application.
Definition at line 5476 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, ast_channel::name, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and ast_channel::uniqueid.
Referenced by load_module().
05477 { 05478 int res=-1; 05479 char *parse, *temppos = NULL; 05480 AST_DECLARE_APP_ARGS(args, 05481 AST_APP_ARG(queuename); 05482 AST_APP_ARG(interface); 05483 AST_APP_ARG(penalty); 05484 AST_APP_ARG(options); 05485 AST_APP_ARG(membername); 05486 AST_APP_ARG(state_interface); 05487 ); 05488 int penalty = 0; 05489 05490 if (ast_strlen_zero(data)) { 05491 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n"); 05492 return -1; 05493 } 05494 05495 parse = ast_strdupa(data); 05496 05497 AST_STANDARD_APP_ARGS(args, parse); 05498 05499 if (ast_strlen_zero(args.interface)) { 05500 args.interface = ast_strdupa(chan->name); 05501 temppos = strrchr(args.interface, '-'); 05502 if (temppos) 05503 *temppos = '\0'; 05504 } 05505 05506 if (!ast_strlen_zero(args.penalty)) { 05507 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) { 05508 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 05509 penalty = 0; 05510 } 05511 } 05512 05513 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) { 05514 case RES_OKAY: 05515 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 05516 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 05517 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 05518 res = 0; 05519 break; 05520 case RES_EXISTS: 05521 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 05522 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 05523 res = 0; 05524 break; 05525 case RES_NOSUCHQUEUE: 05526 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 05527 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 05528 res = 0; 05529 break; 05530 case RES_OUTOFMEMORY: 05531 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 05532 break; 05533 } 05534 05535 return res; 05536 }
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 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 4051 of file app_queue.c.
References ast_channel_datastore_find(), and queue_transfer_info.
04052 { 04053 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 04054 }
static int autopause2int | ( | const char * | autopause | ) | [static] |
Definition at line 1073 of file app_queue.c.
References ARRAY_LEN, ast_strlen_zero(), ast_true(), autopause::autopause, autopausesmodes, QUEUE_AUTOPAUSE_OFF, and QUEUE_AUTOPAUSE_ON.
Referenced by queue_set_param().
01074 { 01075 int x; 01076 /*This 'double check' that default value is OFF */ 01077 if (ast_strlen_zero(autopause)) 01078 return QUEUE_AUTOPAUSE_OFF; 01079 01080 /*This 'double check' is to ensure old values works */ 01081 if(ast_true(autopause)) 01082 return QUEUE_AUTOPAUSE_ON; 01083 01084 for (x = 0; x < ARRAY_LEN(autopausesmodes); x++) { 01085 if (!strcasecmp(autopause, autopausesmodes[x].name)) 01086 return autopausesmodes[x].autopause; 01087 } 01088 01089 /*This 'double check' that default value is OFF */ 01090 return QUEUE_AUTOPAUSE_OFF; 01091 }
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 3880 of file app_queue.c.
References ast_debug, ast_log(), ast_random(), member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, callattempt::metric, queue_ent::min_penalty, member::penalty, call_queue::penaltymemberslimit, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_RRMEMORY, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
03881 { 03882 /* disregarding penalty on too few members? */ 03883 unsigned char usepenalty = (q->membercount <= q->penaltymemberslimit) ? 0 : 1; 03884 03885 if (usepenalty) { 03886 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || 03887 (qe->min_penalty && (mem->penalty < qe->min_penalty))) { 03888 return -1; 03889 } 03890 } else { 03891 ast_debug(1, "Disregarding penalty, %d members and %d in penaltymemberslimit.\n", 03892 q->membercount, q->penaltymemberslimit); 03893 } 03894 03895 switch (q->strategy) { 03896 case QUEUE_STRATEGY_RINGALL: 03897 /* Everyone equal, except for penalty */ 03898 tmp->metric = mem->penalty * 1000000 * usepenalty; 03899 break; 03900 case QUEUE_STRATEGY_LINEAR: 03901 if (pos < qe->linpos) { 03902 tmp->metric = 1000 + pos; 03903 } else { 03904 if (pos > qe->linpos) 03905 /* Indicate there is another priority */ 03906 qe->linwrapped = 1; 03907 tmp->metric = pos; 03908 } 03909 tmp->metric += mem->penalty * 1000000 * usepenalty; 03910 break; 03911 case QUEUE_STRATEGY_RRMEMORY: 03912 if (pos < q->rrpos) { 03913 tmp->metric = 1000 + pos; 03914 } else { 03915 if (pos > q->rrpos) 03916 /* Indicate there is another priority */ 03917 q->wrapped = 1; 03918 tmp->metric = pos; 03919 } 03920 tmp->metric += mem->penalty * 1000000 * usepenalty; 03921 break; 03922 case QUEUE_STRATEGY_RANDOM: 03923 tmp->metric = ast_random() % 1000; 03924 tmp->metric += mem->penalty * 1000000 * usepenalty; 03925 break; 03926 case QUEUE_STRATEGY_WRANDOM: 03927 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000); 03928 break; 03929 case QUEUE_STRATEGY_FEWESTCALLS: 03930 tmp->metric = mem->calls; 03931 tmp->metric += mem->penalty * 1000000 * usepenalty; 03932 break; 03933 case QUEUE_STRATEGY_LEASTRECENT: 03934 if (!mem->lastcall) 03935 tmp->metric = 0; 03936 else 03937 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 03938 tmp->metric += mem->penalty * 1000000 * usepenalty; 03939 break; 03940 default: 03941 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 03942 break; 03943 } 03944 return 0; 03945 }
static void callattempt_free | ( | struct callattempt * | doomed | ) | [static] |
Definition at line 2663 of file app_queue.c.
References ao2_ref, ast_free, ast_party_connected_line_free(), callattempt::connected, and callattempt::member.
Referenced by hangupcalls().
02664 { 02665 if (doomed->member) { 02666 ao2_ref(doomed->member, -1); 02667 } 02668 ast_party_connected_line_free(&doomed->connected); 02669 ast_free(doomed); 02670 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 1573 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().
01574 { 01575 q->holdtime = 0; 01576 q->callscompleted = 0; 01577 q->callsabandoned = 0; 01578 q->callscompletedinsl = 0; 01579 q->talktime = 0; 01580 01581 if (q->members) { 01582 struct member *mem; 01583 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01584 while ((mem = ao2_iterator_next(&mem_iter))) { 01585 mem->calls = 0; 01586 mem->lastcall = 0; 01587 ao2_ref(mem, -1); 01588 } 01589 ao2_iterator_destroy(&mem_iter); 01590 } 01591 }
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 6658 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(), call_queue::name, and queue_t_unref.
Referenced by reload_handler().
06659 { 06660 struct call_queue *q; 06661 struct ao2_iterator queue_iter = ao2_iterator_init(queues, 0); 06662 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06663 ao2_lock(q); 06664 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) 06665 clear_queue(q); 06666 ao2_unlock(q); 06667 queue_t_unref(q, "Done with iterator"); 06668 } 06669 ao2_iterator_destroy(&queue_iter); 06670 return 0; 06671 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 2743 of file app_queue.c.
References ao2_find, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_debug, call_queue::count, member::interface, call_queue::members, call_queue::name, num_available_members(), OBJ_POINTER, queue_t_unref, queues, and call_queue::weight.
Referenced by ring_entry().
02744 { 02745 struct call_queue *q; 02746 struct member *mem; 02747 int found = 0; 02748 struct ao2_iterator queue_iter; 02749 02750 /* q's lock and rq's lock already set by try_calling() 02751 * to solve deadlock */ 02752 queue_iter = ao2_iterator_init(queues, 0); 02753 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 02754 if (q == rq) { /* don't check myself, could deadlock */ 02755 queue_t_unref(q, "Done with iterator"); 02756 continue; 02757 } 02758 ao2_lock(q); 02759 if (q->count && q->members) { 02760 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 02761 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 02762 if (q->weight > rq->weight && q->count >= num_available_members(q)) { 02763 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); 02764 found = 1; 02765 } 02766 ao2_ref(mem, -1); 02767 } 02768 } 02769 ao2_unlock(q); 02770 queue_t_unref(q, "Done with iterator"); 02771 if (found) { 02772 break; 02773 } 02774 } 02775 ao2_iterator_destroy(&queue_iter); 02776 return found; 02777 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6845 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_t_iterator_next, ast_strdup, call_queue::name, and queue_t_unref.
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().
06846 { 06847 struct call_queue *q; 06848 char *ret = NULL; 06849 int which = 0; 06850 int wordlen = strlen(word); 06851 struct ao2_iterator queue_iter; 06852 06853 queue_iter = ao2_iterator_init(queues, 0); 06854 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06855 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 06856 ret = ast_strdup(q->name); 06857 queue_t_unref(q, "Done with iterator"); 06858 break; 06859 } 06860 queue_t_unref(q, "Done with iterator"); 06861 } 06862 ao2_iterator_destroy(&queue_iter); 06863 06864 return ret; 06865 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7277 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
Referenced by handle_queue_add_member().
07278 { 07279 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 07280 switch (pos) { 07281 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 07282 return NULL; 07283 case 4: /* only one possible match, "to" */ 07284 return state == 0 ? ast_strdup("to") : NULL; 07285 case 5: /* <queue> */ 07286 return complete_queue(line, word, pos, state); 07287 case 6: /* only one possible match, "penalty" */ 07288 return state == 0 ? ast_strdup("penalty") : NULL; 07289 case 7: 07290 if (state < 100) { /* 0-99 */ 07291 char *num; 07292 if ((num = ast_malloc(3))) { 07293 sprintf(num, "%d", state); 07294 } 07295 return num; 07296 } else { 07297 return NULL; 07298 } 07299 case 8: /* only one possible match, "as" */ 07300 return state == 0 ? ast_strdup("as") : NULL; 07301 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 07302 return NULL; 07303 default: 07304 return NULL; 07305 } 07306 }
static char* complete_queue_pause_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7498 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_pause_member().
07499 { 07500 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */ 07501 switch (pos) { 07502 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 07503 return NULL; 07504 case 4: /* only one possible match, "queue" */ 07505 return state == 0 ? ast_strdup("queue") : NULL; 07506 case 5: /* <queue> */ 07507 return complete_queue(line, word, pos, state); 07508 case 6: /* "reason" */ 07509 return state == 0 ? ast_strdup("reason") : NULL; 07510 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */ 07511 return NULL; 07512 default: 07513 return NULL; 07514 } 07515 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7407 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, and queue_t_unref.
Referenced by handle_queue_remove_member().
07408 { 07409 int which = 0; 07410 struct call_queue *q; 07411 struct member *m; 07412 struct ao2_iterator queue_iter; 07413 struct ao2_iterator mem_iter; 07414 int wordlen = strlen(word); 07415 07416 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 07417 if (pos > 5 || pos < 3) 07418 return NULL; 07419 if (pos == 4) /* only one possible match, 'from' */ 07420 return (state == 0 ? ast_strdup("from") : NULL); 07421 07422 if (pos == 5) /* No need to duplicate code */ 07423 return complete_queue(line, word, pos, state); 07424 07425 /* here is the case for 3, <member> */ 07426 queue_iter = ao2_iterator_init(queues, 0); 07427 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07428 ao2_lock(q); 07429 mem_iter = ao2_iterator_init(q->members, 0); 07430 while ((m = ao2_iterator_next(&mem_iter))) { 07431 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) { 07432 char *tmp; 07433 ao2_unlock(q); 07434 tmp = ast_strdup(m->interface); 07435 ao2_ref(m, -1); 07436 queue_t_unref(q, "Done with iterator, returning interface name"); 07437 ao2_iterator_destroy(&mem_iter); 07438 ao2_iterator_destroy(&queue_iter); 07439 return tmp; 07440 } 07441 ao2_ref(m, -1); 07442 } 07443 ao2_iterator_destroy(&mem_iter); 07444 ao2_unlock(q); 07445 queue_t_unref(q, "Done with iterator"); 07446 } 07447 ao2_iterator_destroy(&queue_iter); 07448 07449 return NULL; 07450 }
static char* complete_queue_rule_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7631 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, rule_list::list, and rule_list::name.
Referenced by handle_queue_rule_show().
07632 { 07633 int which = 0; 07634 struct rule_list *rl_iter; 07635 int wordlen = strlen(word); 07636 char *ret = NULL; 07637 if (pos != 3) /* Wha? */ { 07638 return NULL; 07639 } 07640 07641 AST_LIST_LOCK(&rule_lists); 07642 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 07643 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) { 07644 ret = ast_strdup(rl_iter->name); 07645 break; 07646 } 07647 } 07648 AST_LIST_UNLOCK(&rule_lists); 07649 07650 return ret; 07651 }
static char* complete_queue_set_member_penalty | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 7568 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_set_member_penalty().
07569 { 07570 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/ 07571 switch (pos) { 07572 case 4: 07573 if (state == 0) { 07574 return ast_strdup("on"); 07575 } else { 07576 return NULL; 07577 } 07578 case 6: 07579 if (state == 0) { 07580 return ast_strdup("in"); 07581 } else { 07582 return NULL; 07583 } 07584 case 7: 07585 return complete_queue(line, word, pos, state); 07586 default: 07587 return NULL; 07588 } 07589 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6867 of file app_queue.c.
References complete_queue().
Referenced by queue_show().
06868 { 06869 if (pos == 2) 06870 return complete_queue(line, word, pos, state); 06871 return NULL; 06872 }
static int compress_char | ( | const char | c | ) | [static] |
Definition at line 1470 of file app_queue.c.
Referenced by member_hash_fn().
01471 { 01472 if (c < 32) 01473 return 0; 01474 else if (c > 96) 01475 return c - 64; 01476 else 01477 return c - 32; 01478 }
static void copy_rules | ( | struct queue_ent * | qe, | |
const char * | rulename | |||
) | [static] |
Copy rule from global list into specified queue.
Definition at line 5573 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(), call_queue::defaultrule, penalty_rule::list, rule_list::list, LOG_ERROR, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, queue_ent::parent, queue_ent::qe_rules, rule_list::rules, and penalty_rule::time.
Referenced by queue_exec().
05574 { 05575 struct penalty_rule *pr_iter; 05576 struct rule_list *rl_iter; 05577 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename; 05578 AST_LIST_LOCK(&rule_lists); 05579 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 05580 if (!strcasecmp(rl_iter->name, tmp)) 05581 break; 05582 } 05583 if (rl_iter) { 05584 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 05585 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr)); 05586 if (!new_pr) { 05587 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n"); 05588 AST_LIST_UNLOCK(&rule_lists); 05589 break; 05590 } 05591 new_pr->time = pr_iter->time; 05592 new_pr->max_value = pr_iter->max_value; 05593 new_pr->min_value = pr_iter->min_value; 05594 new_pr->max_relative = pr_iter->max_relative; 05595 new_pr->min_relative = pr_iter->min_relative; 05596 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list); 05597 } 05598 } 05599 AST_LIST_UNLOCK(&rule_lists); 05600 }
static struct member* create_queue_member | ( | const char * | interface, | |
const char * | membername, | |||
int | penalty, | |||
int | paused, | |||
const char * | state_interface | |||
) | [static] |
allocate space for new queue member and set fields based on parameters passed
Definition at line 1438 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(), LOG_WARNING, member::penalty, S_OR, and strsep().
Referenced by add_to_queue(), reload_single_member(), and rt_handle_member_record().
01439 { 01440 struct member *cur; 01441 01442 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 01443 cur->penalty = penalty; 01444 cur->paused = paused; 01445 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 01446 if (!ast_strlen_zero(state_interface)) 01447 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); 01448 else 01449 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface)); 01450 if (!ast_strlen_zero(membername)) 01451 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 01452 else 01453 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 01454 if (!strchr(cur->interface, '/')) 01455 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 01456 if (!strncmp(cur->state_interface, "hint:", 5)) { 01457 char *tmp = ast_strdupa(cur->state_interface), *context = tmp; 01458 char *exten = strsep(&context, "@") + 5; 01459 01460 ast_copy_string(cur->state_exten, exten, sizeof(cur->state_exten)); 01461 ast_copy_string(cur->state_context, S_OR(context, "default"), sizeof(cur->state_context)); 01462 } 01463 cur->status = get_queue_member_status(cur); 01464 } 01465 01466 return cur; 01467 }
static void destroy_queue | ( | void * | obj | ) | [static] |
Free queue's member list then its string fields.
Definition at line 1994 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().
01995 { 01996 struct call_queue *q = obj; 01997 int i; 01998 01999 free_members(q, 1); 02000 ast_string_field_free_memory(q); 02001 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 02002 if (q->sound_periodicannounce[i]) 02003 free(q->sound_periodicannounce[i]); 02004 } 02005 ao2_ref(q->members, -1); 02006 }
static void device_state_cb | ( | const struct ast_event * | event, | |
void * | unused | |||
) | [static] |
Definition at line 1336 of file app_queue.c.
References ast_calloc, ast_device_state(), 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, devicestate_tps, handle_statechange(), LOG_ERROR, and statechange::state.
Referenced by load_module(), and load_pbx().
01337 { 01338 enum ast_device_state state; 01339 const char *device; 01340 struct statechange *sc; 01341 size_t datapsize; 01342 01343 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); 01344 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); 01345 01346 if (ast_strlen_zero(device)) { 01347 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n"); 01348 return; 01349 } 01350 datapsize = sizeof(*sc) + strlen(device) + 1; 01351 if (!(sc = ast_calloc(1, datapsize))) { 01352 ast_log(LOG_ERROR, "failed to calloc a state change struct\n"); 01353 return; 01354 } 01355 sc->state = state; 01356 strcpy(sc->dev, device); 01357 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) { 01358 ast_free(sc); 01359 } 01360 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 2780 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry().
02781 { 02782 o->stillgoing = 0; 02783 ast_hangup(o->chan); 02784 o->chan = NULL; 02785 }
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 6703 of file app_queue.c.
References ast_cli(), and astman_append().
Referenced by __queues_show().
06704 { 06705 if (s) 06706 astman_append(s, "%s\r\n", str); 06707 else 06708 ast_cli(fd, "%s\n", str); 06709 }
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 4938 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, PM_MAX_LEN, member::state_interface, and value.
Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().
04939 { 04940 struct member *cur_member; 04941 char value[PM_MAX_LEN]; 04942 int value_len = 0; 04943 int res; 04944 struct ao2_iterator mem_iter; 04945 04946 memset(value, 0, sizeof(value)); 04947 04948 if (!pm_queue) 04949 return; 04950 04951 mem_iter = ao2_iterator_init(pm_queue->members, 0); 04952 while ((cur_member = ao2_iterator_next(&mem_iter))) { 04953 if (!cur_member->dynamic) { 04954 ao2_ref(cur_member, -1); 04955 continue; 04956 } 04957 04958 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s", 04959 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface); 04960 04961 ao2_ref(cur_member, -1); 04962 04963 if (res != strlen(value + value_len)) { 04964 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 04965 break; 04966 } 04967 value_len += res; 04968 } 04969 ao2_iterator_destroy(&mem_iter); 04970 04971 if (value_len && !cur_member) { 04972 if (ast_db_put(pm_family, pm_queue->name, value)) 04973 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 04974 } else 04975 /* Delete the entry if the queue is empty or there is an error */ 04976 ast_db_del(pm_family, pm_queue->name); 04977 }
static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 4098 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, queue_end_bridge::q, queue_t_unref, and set_queue_variables().
04099 { 04100 struct queue_end_bridge *qeb = data; 04101 struct call_queue *q = qeb->q; 04102 struct ast_channel *chan = qeb->chan; 04103 04104 if (ao2_ref(qeb, -1) == 1) { 04105 set_queue_variables(q, chan); 04106 /* This unrefs the reference we made in try_calling when we allocated qeb */ 04107 queue_t_unref(q, "Expire bridge_config reference"); 04108 } 04109 }
static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, | |
struct ast_channel * | originator, | |||
struct ast_channel * | terminator | |||
) | [static] |
Definition at line 4091 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, and ast_bridge_config::end_bridge_callback_data.
04092 { 04093 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data; 04094 ao2_ref(qeb, +1); 04095 qeb->chan = originator; 04096 }
static int extension_state_cb | ( | char * | context, | |
char * | exten, | |||
enum ast_extension_states | state, | |||
void * | data | |||
) | [static] |
Definition at line 1394 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().
01395 { 01396 struct ao2_iterator miter, qiter; 01397 struct member *m; 01398 struct call_queue *q; 01399 int found = 0, device_state = extensionstate2devicestate(state); 01400 01401 qiter = ao2_iterator_init(queues, 0); 01402 while ((q = ao2_t_iterator_next(&qiter, "Iterate through queues"))) { 01403 ao2_lock(q); 01404 01405 miter = ao2_iterator_init(q->members, 0); 01406 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01407 if (!strcmp(m->state_context, context) && !strcmp(m->state_exten, exten)) { 01408 update_status(q, m, device_state); 01409 ao2_ref(m, -1); 01410 found = 1; 01411 break; 01412 } 01413 } 01414 ao2_iterator_destroy(&miter); 01415 01416 ao2_unlock(q); 01417 queue_t_unref(q, "Done with iterator"); 01418 } 01419 ao2_iterator_destroy(&qiter); 01420 01421 if (found) { 01422 ast_debug(1, "Extension '%s@%s' changed to state '%d' (%s)\n", exten, context, device_state, ast_devstate2str(device_state)); 01423 } else { 01424 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", 01425 exten, context, device_state, ast_devstate2str(device_state)); 01426 } 01427 01428 return 0; 01429 }
static int extensionstate2devicestate | ( | int | state | ) | [static] |
Helper function which converts from extension state to device state values.
Definition at line 1363 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().
01364 { 01365 switch (state) { 01366 case AST_EXTENSION_NOT_INUSE: 01367 state = AST_DEVICE_NOT_INUSE; 01368 break; 01369 case AST_EXTENSION_INUSE: 01370 state = AST_DEVICE_INUSE; 01371 break; 01372 case AST_EXTENSION_BUSY: 01373 state = AST_DEVICE_BUSY; 01374 break; 01375 case AST_EXTENSION_RINGING: 01376 state = AST_DEVICE_RINGING; 01377 break; 01378 case AST_EXTENSION_ONHOLD: 01379 state = AST_DEVICE_ONHOLD; 01380 break; 01381 case AST_EXTENSION_UNAVAILABLE: 01382 state = AST_DEVICE_UNAVAILABLE; 01383 break; 01384 case AST_EXTENSION_REMOVED: 01385 case AST_EXTENSION_DEACTIVATED: 01386 default: 01387 state = AST_DEVICE_INVALID; 01388 break; 01389 } 01390 01391 return state; 01392 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static] |
find the entry with the best metric, or NULL
Definition at line 3014 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
Referenced by ast_cli_command_full(), ring_one(), store_next_lin(), and store_next_rr().
03015 { 03016 struct callattempt *best = NULL, *cur; 03017 03018 for (cur = outgoing; cur; cur = cur->q_next) { 03019 if (cur->stillgoing && /* Not already done */ 03020 !cur->chan && /* Isn't already going */ 03021 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 03022 best = cur; 03023 } 03024 } 03025 03026 return best; 03027 }
static struct call_queue* find_queue_by_name_rt | ( | const char * | queuename, | |
struct ast_variable * | queue_vars, | |||
struct ast_config * | member_config | |||
) | [static] |
Reload a single queue via realtime.
Check for statically defined queue first, check if deleted RT queue, check for new RT queue, if queue vars are not defined init them with defaults. reload RT queue vars, set RT queue members dead and reload them, return finished queue.
the | queue, | |
NULL | if it doesn't exist. |
Definition at line 2032 of file app_queue.c.
References alloc_queue(), ao2_lock, ao2_t_find, ao2_unlock, ast_debug, ast_log(), clear_queue(), call_queue::dead, LOG_WARNING, call_queue::membercount, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, strat2int(), call_queue::strategy, and ast_variable::value.
Referenced by load_realtime_queue().
02033 { 02034 struct ast_variable *v; 02035 struct call_queue *q, tmpq = { 02036 .name = queuename, 02037 }; 02038 struct member *m; 02039 struct ao2_iterator mem_iter; 02040 char *interface = NULL; 02041 const char *tmp_name; 02042 char *tmp; 02043 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 02044 02045 /* Static queues override realtime. */ 02046 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) { 02047 ao2_lock(q); 02048 if (!q->realtime) { 02049 if (q->dead) { 02050 ao2_unlock(q); 02051 queue_t_unref(q, "Queue is dead; can't return it"); 02052 return NULL; 02053 } else { 02054 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 02055 ao2_unlock(q); 02056 return q; 02057 } 02058 } 02059 } else if (!member_config) 02060 /* Not found in the list, and it's not realtime ... */ 02061 return NULL; 02062 02063 /* Check if queue is defined in realtime. */ 02064 if (!queue_vars) { 02065 /* Delete queue from in-core list if it has been deleted in realtime. */ 02066 if (q) { 02067 /*! \note Hmm, can't seem to distinguish a DB failure from a not 02068 found condition... So we might delete an in-core queue 02069 in case of DB failure. */ 02070 ast_debug(1, "Queue %s not found in realtime.\n", queuename); 02071 02072 q->dead = 1; 02073 /* Delete if unused (else will be deleted when last caller leaves). */ 02074 queues_t_unlink(queues, q, "Unused; removing from container"); 02075 ao2_unlock(q); 02076 queue_t_unref(q, "Queue is dead; can't return it"); 02077 } 02078 return NULL; 02079 } 02080 02081 /* Create a new queue if an in-core entry does not exist yet. */ 02082 if (!q) { 02083 struct ast_variable *tmpvar = NULL; 02084 if (!(q = alloc_queue(queuename))) 02085 return NULL; 02086 ao2_lock(q); 02087 clear_queue(q); 02088 q->realtime = 1; 02089 q->membercount = 0; 02090 /*Before we initialize the queue, we need to set the strategy, so that linear strategy 02091 * will allocate the members properly 02092 */ 02093 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { 02094 if (!strcasecmp(tmpvar->name, "strategy")) { 02095 q->strategy = strat2int(tmpvar->value); 02096 if (q->strategy < 0) { 02097 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 02098 tmpvar->value, q->name); 02099 q->strategy = QUEUE_STRATEGY_RINGALL; 02100 } 02101 break; 02102 } 02103 } 02104 /* We traversed all variables and didn't find a strategy */ 02105 if (!tmpvar) 02106 q->strategy = QUEUE_STRATEGY_RINGALL; 02107 queues_t_link(queues, q, "Add queue to container"); 02108 } 02109 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 02110 02111 memset(tmpbuf, 0, sizeof(tmpbuf)); 02112 for (v = queue_vars; v; v = v->next) { 02113 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 02114 if ((tmp = strchr(v->name, '_'))) { 02115 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 02116 tmp_name = tmpbuf; 02117 tmp = tmpbuf; 02118 while ((tmp = strchr(tmp, '_'))) 02119 *tmp++ = '-'; 02120 } else 02121 tmp_name = v->name; 02122 02123 /* NULL values don't get returned from realtime; blank values should 02124 * still get set. If someone doesn't want a value to be set, they 02125 * should set the realtime column to NULL, not blank. */ 02126 queue_set_param(q, tmp_name, v->value, -1, 0); 02127 } 02128 02129 /* Temporarily set realtime members dead so we can detect deleted ones. 02130 * Also set the membercount correctly for realtime*/ 02131 mem_iter = ao2_iterator_init(q->members, 0); 02132 while ((m = ao2_iterator_next(&mem_iter))) { 02133 q->membercount++; 02134 if (m->realtime) 02135 m->dead = 1; 02136 ao2_ref(m, -1); 02137 } 02138 ao2_iterator_destroy(&mem_iter); 02139 02140 while ((interface = ast_category_browse(member_config, interface))) { 02141 rt_handle_member_record(q, interface, 02142 ast_variable_retrieve(member_config, interface, "uniqueid"), 02143 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface), 02144 ast_variable_retrieve(member_config, interface, "penalty"), 02145 ast_variable_retrieve(member_config, interface, "paused"), 02146 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface)); 02147 } 02148 02149 /* Delete all realtime members that have been deleted in DB. */ 02150 mem_iter = ao2_iterator_init(q->members, 0); 02151 while ((m = ao2_iterator_next(&mem_iter))) { 02152 if (m->dead) { 02153 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 02154 ao2_unlink(q->members, m); 02155 q->membercount--; 02156 } 02157 ao2_ref(m, -1); 02158 } 02159 ao2_iterator_destroy(&mem_iter); 02160 02161 ao2_unlock(q); 02162 02163 return q; 02164 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Iterate through queue's member list and delete them.
Definition at line 1977 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, member::dynamic, call_queue::membercount, and call_queue::members.
Referenced by destroy_queue().
01978 { 01979 /* Free non-dynamic members */ 01980 struct member *cur; 01981 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01982 01983 while ((cur = ao2_iterator_next(&mem_iter))) { 01984 if (all || !cur->dynamic) { 01985 ao2_unlink(q->members, cur); 01986 q->membercount--; 01987 } 01988 ao2_ref(cur, -1); 01989 } 01990 ao2_iterator_destroy(&mem_iter); 01991 }
static int get_member_penalty | ( | char * | queuename, | |
char * | interface | |||
) | [static] |
Definition at line 5218 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, and RESULT_FAILURE.
Referenced by queue_function_memberpenalty_read().
05219 { 05220 int foundqueue = 0, penalty; 05221 struct call_queue *q, tmpq = { 05222 .name = queuename, 05223 }; 05224 struct member *mem; 05225 05226 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) { 05227 foundqueue = 1; 05228 ao2_lock(q); 05229 if ((mem = interface_exists(q, interface))) { 05230 penalty = mem->penalty; 05231 ao2_ref(mem, -1); 05232 ao2_unlock(q); 05233 queue_t_unref(q, "Search complete"); 05234 return penalty; 05235 } 05236 ao2_unlock(q); 05237 queue_t_unref(q, "Search complete"); 05238 } 05239 05240 /* some useful debuging */ 05241 if (foundqueue) 05242 ast_log (LOG_ERROR, "Invalid queuename\n"); 05243 else 05244 ast_log (LOG_ERROR, "Invalid interface\n"); 05245 05246 return RESULT_FAILURE; 05247 }
static int get_member_status | ( | struct call_queue * | q, | |
int | max_penalty, | |||
int | min_penalty, | |||
enum empty_conditions | conditions | |||
) | [static] |
Check if members are available.
This function checks to see if members are available to be called. If any member is available, the function immediately returns 0. If no members are available, then -1 is returned.
Definition at line 1187 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_RINGING, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, member::lastcall, member::membername, call_queue::members, member::paused, member::penalty, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_RINGING, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, QUEUE_EMPTY_WRAPUP, member::status, and call_queue::wrapuptime.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
01188 { 01189 struct member *member; 01190 struct ao2_iterator mem_iter; 01191 01192 ao2_lock(q); 01193 mem_iter = ao2_iterator_init(q->members, 0); 01194 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) { 01195 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) { 01196 if (conditions & QUEUE_EMPTY_PENALTY) { 01197 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty); 01198 continue; 01199 } 01200 } 01201 01202 switch (member->status) { 01203 case AST_DEVICE_INVALID: 01204 if (conditions & QUEUE_EMPTY_INVALID) { 01205 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername); 01206 break; 01207 } 01208 goto default_case; 01209 case AST_DEVICE_UNAVAILABLE: 01210 if (conditions & QUEUE_EMPTY_UNAVAILABLE) { 01211 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername); 01212 break; 01213 } 01214 goto default_case; 01215 case AST_DEVICE_INUSE: 01216 if (conditions & QUEUE_EMPTY_INUSE) { 01217 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername); 01218 break; 01219 } 01220 goto default_case; 01221 case AST_DEVICE_RINGING: 01222 if (conditions & QUEUE_EMPTY_RINGING) { 01223 ast_debug(4, "%s is unavailable because his device state is 'ringing'\n", member->membername); 01224 break; 01225 } 01226 case AST_DEVICE_UNKNOWN: 01227 if (conditions & QUEUE_EMPTY_UNKNOWN) { 01228 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername); 01229 break; 01230 } 01231 /* Fall-through */ 01232 default: 01233 default_case: 01234 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) { 01235 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername); 01236 break; 01237 } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) { 01238 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); 01239 break; 01240 } else { 01241 ao2_unlock(q); 01242 ao2_ref(member, -1); 01243 ao2_iterator_destroy(&mem_iter); 01244 ast_debug(4, "%s is available.\n", member->membername); 01245 return 0; 01246 } 01247 break; 01248 } 01249 } 01250 ao2_iterator_destroy(&mem_iter); 01251 01252 ao2_unlock(q); 01253 return -1; 01254 }
static int get_queue_member_status | ( | struct member * | cur | ) | [static] |
Return the current state of a member.
Definition at line 1432 of file app_queue.c.
References ast_device_state(), ast_extension_state(), ast_strlen_zero(), extensionstate2devicestate(), member::state_context, member::state_exten, and member::state_interface.
Referenced by create_queue_member(), kill_dead_members(), and ring_entry().
01433 { 01434 return ast_strlen_zero(cur->state_exten) ? ast_device_state(cur->state_interface) : extensionstate2devicestate(ast_extension_state(NULL, cur->state_context, cur->state_exten)); 01435 }
static char* handle_queue_add_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7333 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.
07334 { 07335 const char *queuename, *interface, *membername = NULL, *state_interface = NULL; 07336 int penalty; 07337 07338 switch ( cmd ) { 07339 case CLI_INIT: 07340 e->command = "queue add member"; 07341 e->usage = 07342 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n" 07343 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n"; 07344 return NULL; 07345 case CLI_GENERATE: 07346 return complete_queue_add_member(a->line, a->word, a->pos, a->n); 07347 } 07348 07349 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) { 07350 return CLI_SHOWUSAGE; 07351 } else if (strcmp(a->argv[4], "to")) { 07352 return CLI_SHOWUSAGE; 07353 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) { 07354 return CLI_SHOWUSAGE; 07355 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) { 07356 return CLI_SHOWUSAGE; 07357 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) { 07358 return CLI_SHOWUSAGE; 07359 } 07360 07361 queuename = a->argv[5]; 07362 interface = a->argv[3]; 07363 if (a->argc >= 8) { 07364 if (sscanf(a->argv[7], "%30d", &penalty) == 1) { 07365 if (penalty < 0) { 07366 ast_cli(a->fd, "Penalty must be >= 0\n"); 07367 penalty = 0; 07368 } 07369 } else { 07370 ast_cli(a->fd, "Penalty must be an integer >= 0\n"); 07371 penalty = 0; 07372 } 07373 } else { 07374 penalty = 0; 07375 } 07376 07377 if (a->argc >= 10) { 07378 membername = a->argv[9]; 07379 } 07380 07381 if (a->argc >= 12) { 07382 state_interface = a->argv[11]; 07383 } 07384 07385 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) { 07386 case RES_OKAY: 07387 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 07388 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 07389 return CLI_SUCCESS; 07390 case RES_EXISTS: 07391 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 07392 return CLI_FAILURE; 07393 case RES_NOSUCHQUEUE: 07394 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 07395 return CLI_FAILURE; 07396 case RES_OUTOFMEMORY: 07397 ast_cli(a->fd, "Out of memory\n"); 07398 return CLI_FAILURE; 07399 case RES_NOT_DYNAMIC: 07400 ast_cli(a->fd, "Member not dynamic\n"); 07401 return CLI_FAILURE; 07402 default: 07403 return CLI_FAILURE; 07404 } 07405 }
static char* handle_queue_pause_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7517 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.
07518 { 07519 const char *queuename, *interface, *reason; 07520 int paused; 07521 07522 switch (cmd) { 07523 case CLI_INIT: 07524 e->command = "queue {pause|unpause} member"; 07525 e->usage = 07526 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n" 07527 " Pause or unpause a queue member. Not specifying a particular queue\n" 07528 " will pause or unpause a member across all queues to which the member\n" 07529 " belongs.\n"; 07530 return NULL; 07531 case CLI_GENERATE: 07532 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n); 07533 } 07534 07535 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) { 07536 return CLI_SHOWUSAGE; 07537 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) { 07538 return CLI_SHOWUSAGE; 07539 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) { 07540 return CLI_SHOWUSAGE; 07541 } 07542 07543 07544 interface = a->argv[3]; 07545 queuename = a->argc >= 6 ? a->argv[5] : NULL; 07546 reason = a->argc == 8 ? a->argv[7] : NULL; 07547 paused = !strcasecmp(a->argv[1], "pause"); 07548 07549 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) { 07550 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface); 07551 if (!ast_strlen_zero(queuename)) 07552 ast_cli(a->fd, " in queue '%s'", queuename); 07553 if (!ast_strlen_zero(reason)) 07554 ast_cli(a->fd, " for reason '%s'", reason); 07555 ast_cli(a->fd, "\n"); 07556 return CLI_SUCCESS; 07557 } else { 07558 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface); 07559 if (!ast_strlen_zero(queuename)) 07560 ast_cli(a->fd, " in queue '%s'", queuename); 07561 if (!ast_strlen_zero(reason)) 07562 ast_cli(a->fd, " for reason '%s'", reason); 07563 ast_cli(a->fd, "\n"); 07564 return CLI_FAILURE; 07565 } 07566 }
static char* handle_queue_reload | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7726 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.
07727 { 07728 struct ast_flags mask = {0,}; 07729 int i; 07730 07731 switch (cmd) { 07732 case CLI_INIT: 07733 e->command = "queue reload {parameters|members|rules|all}"; 07734 e->usage = 07735 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n" 07736 "Reload queues. If <queuenames> are specified, only reload information pertaining\n" 07737 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n" 07738 "specified in order to know what information to reload. Below is an explanation\n" 07739 "of each of these qualifiers.\n" 07740 "\n" 07741 "\t'members' - reload queue members from queues.conf\n" 07742 "\t'parameters' - reload all queue options except for queue members\n" 07743 "\t'rules' - reload the queuerules.conf file\n" 07744 "\t'all' - reload queue rules, parameters, and members\n" 07745 "\n" 07746 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n" 07747 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n" 07748 "one queue is specified when using this command, reloading queue rules may cause\n" 07749 "other queues to be affected\n"; 07750 return NULL; 07751 case CLI_GENERATE: 07752 if (a->pos >= 3) { 07753 return complete_queue(a->line, a->word, a->pos, a->n); 07754 } else { 07755 return NULL; 07756 } 07757 } 07758 07759 if (a->argc < 3) 07760 return CLI_SHOWUSAGE; 07761 07762 if (!strcasecmp(a->argv[2], "rules")) { 07763 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 07764 } else if (!strcasecmp(a->argv[2], "members")) { 07765 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 07766 } else if (!strcasecmp(a->argv[2], "parameters")) { 07767 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 07768 } else if (!strcasecmp(a->argv[2], "all")) { 07769 ast_set_flag(&mask, AST_FLAGS_ALL); 07770 } 07771 07772 if (a->argc == 3) { 07773 reload_handler(1, &mask, NULL); 07774 return CLI_SUCCESS; 07775 } 07776 07777 for (i = 3; i < a->argc; ++i) { 07778 reload_handler(1, &mask, a->argv[i]); 07779 } 07780 07781 return CLI_SUCCESS; 07782 }
static char* handle_queue_remove_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7452 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.
07453 { 07454 const char *queuename, *interface; 07455 07456 switch (cmd) { 07457 case CLI_INIT: 07458 e->command = "queue remove member"; 07459 e->usage = 07460 "Usage: queue remove member <channel> from <queue>\n" 07461 " Remove a specific channel from a queue.\n"; 07462 return NULL; 07463 case CLI_GENERATE: 07464 return complete_queue_remove_member(a->line, a->word, a->pos, a->n); 07465 } 07466 07467 if (a->argc != 6) { 07468 return CLI_SHOWUSAGE; 07469 } else if (strcmp(a->argv[4], "from")) { 07470 return CLI_SHOWUSAGE; 07471 } 07472 07473 queuename = a->argv[5]; 07474 interface = a->argv[3]; 07475 07476 switch (remove_from_queue(queuename, interface)) { 07477 case RES_OKAY: 07478 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 07479 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 07480 return CLI_SUCCESS; 07481 case RES_EXISTS: 07482 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 07483 return CLI_FAILURE; 07484 case RES_NOSUCHQUEUE: 07485 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 07486 return CLI_FAILURE; 07487 case RES_OUTOFMEMORY: 07488 ast_cli(a->fd, "Out of memory\n"); 07489 return CLI_FAILURE; 07490 case RES_NOT_DYNAMIC: 07491 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename); 07492 return CLI_FAILURE; 07493 default: 07494 return CLI_FAILURE; 07495 } 07496 }
static char* handle_queue_reset | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7687 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.
07688 { 07689 struct ast_flags mask = {QUEUE_RESET_STATS,}; 07690 int i; 07691 07692 switch (cmd) { 07693 case CLI_INIT: 07694 e->command = "queue reset stats"; 07695 e->usage = 07696 "Usage: queue reset stats [<queuenames>]\n" 07697 "\n" 07698 "Issuing this command will reset statistics for\n" 07699 "<queuenames>, or for all queues if no queue is\n" 07700 "specified.\n"; 07701 return NULL; 07702 case CLI_GENERATE: 07703 if (a->pos >= 3) { 07704 return complete_queue(a->line, a->word, a->pos, a->n); 07705 } else { 07706 return NULL; 07707 } 07708 } 07709 07710 if (a->argc < 3) { 07711 return CLI_SHOWUSAGE; 07712 } 07713 07714 if (a->argc == 3) { 07715 reload_handler(1, &mask, NULL); 07716 return CLI_SUCCESS; 07717 } 07718 07719 for (i = 3; i < a->argc; ++i) { 07720 reload_handler(1, &mask, a->argv[i]); 07721 } 07722 07723 return CLI_SUCCESS; 07724 }
static char* handle_queue_rule_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7653 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_rule_show(), ast_cli_args::fd, ast_cli_args::line, penalty_rule::list, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, ast_cli_args::n, rule_list::name, ast_cli_args::pos, rule_list::rules, penalty_rule::time, ast_cli_entry::usage, and ast_cli_args::word.
07654 { 07655 const char *rule; 07656 struct rule_list *rl_iter; 07657 struct penalty_rule *pr_iter; 07658 switch (cmd) { 07659 case CLI_INIT: 07660 e->command = "queue show rules"; 07661 e->usage = 07662 "Usage: queue show rules [rulename]\n" 07663 " Show the list of rules associated with rulename. If no\n" 07664 " rulename is specified, list all rules defined in queuerules.conf\n"; 07665 return NULL; 07666 case CLI_GENERATE: 07667 return complete_queue_rule_show(a->line, a->word, a->pos, a->n); 07668 } 07669 07670 if (a->argc != 3 && a->argc != 4) 07671 return CLI_SHOWUSAGE; 07672 07673 rule = a->argc == 4 ? a->argv[3] : ""; 07674 AST_LIST_LOCK(&rule_lists); 07675 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 07676 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) { 07677 ast_cli(a->fd, "Rule: %s\n", rl_iter->name); 07678 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 07679 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); 07680 } 07681 } 07682 } 07683 AST_LIST_UNLOCK(&rule_lists); 07684 return CLI_SUCCESS; 07685 }
static char* handle_queue_set_member_penalty | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 7591 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.
07592 { 07593 const char *queuename = NULL, *interface; 07594 int penalty = 0; 07595 07596 switch (cmd) { 07597 case CLI_INIT: 07598 e->command = "queue set penalty"; 07599 e->usage = 07600 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n" 07601 " Set a member's penalty in the queue specified. If no queue is specified\n" 07602 " then that interface's penalty is set in all queues to which that interface is a member\n"; 07603 return NULL; 07604 case CLI_GENERATE: 07605 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n); 07606 } 07607 07608 if (a->argc != 6 && a->argc != 8) { 07609 return CLI_SHOWUSAGE; 07610 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) { 07611 return CLI_SHOWUSAGE; 07612 } 07613 07614 if (a->argc == 8) 07615 queuename = a->argv[7]; 07616 interface = a->argv[5]; 07617 penalty = atoi(a->argv[3]); 07618 07619 switch (set_member_penalty(queuename, interface, penalty)) { 07620 case RESULT_SUCCESS: 07621 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); 07622 return CLI_SUCCESS; 07623 case RESULT_FAILURE: 07624 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); 07625 return CLI_FAILURE; 07626 default: 07627 return CLI_FAILURE; 07628 } 07629 }
static int handle_statechange | ( | void * | datap | ) | [static] |
set a member's status based on device state of that member's interface
Definition at line 1292 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, statechange::state, member::state_interface, and update_status().
Referenced by device_state_cb().
01293 { 01294 struct statechange *sc = datap; 01295 struct ao2_iterator miter, qiter; 01296 struct member *m; 01297 struct call_queue *q; 01298 char interface[80], *slash_pos; 01299 int found = 0; 01300 01301 qiter = ao2_iterator_init(queues, 0); 01302 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) { 01303 ao2_lock(q); 01304 01305 miter = ao2_iterator_init(q->members, 0); 01306 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01307 ast_copy_string(interface, m->state_interface, sizeof(interface)); 01308 01309 if ((slash_pos = strchr(interface, '/'))) 01310 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) 01311 *slash_pos = '\0'; 01312 01313 if (!strcasecmp(interface, sc->dev)) { 01314 found = 1; 01315 update_status(q, m, sc->state); 01316 ao2_ref(m, -1); 01317 break; 01318 } 01319 } 01320 ao2_iterator_destroy(&miter); 01321 01322 ao2_unlock(q); 01323 queue_t_unref(q, "Done with iterator"); 01324 } 01325 ao2_iterator_destroy(&qiter); 01326 01327 if (found) 01328 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state)); 01329 else 01330 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)); 01331 01332 ast_free(sc); 01333 return 0; 01334 }
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 2673 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.
02674 { 02675 struct callattempt *oo; 02676 02677 while (outgoing) { 02678 /* If someone else answered the call we should indicate this in the CANCEL */ 02679 /* Hangup any existing lines we have open */ 02680 if (outgoing->chan && (outgoing->chan != exception)) { 02681 if (exception || cancel_answered_elsewhere) 02682 ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE); 02683 ast_hangup(outgoing->chan); 02684 } 02685 oo = outgoing; 02686 outgoing = outgoing->q_next; 02687 ast_aoc_destroy_decoded(oo->aoc_s_rate_list); 02688 callattempt_free(oo); 02689 } 02690 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Initialize Queue default values.
Definition at line 1502 of file app_queue.c.
References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::autopause, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, penalty_rule::list, 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, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by reload_single_queue().
01503 { 01504 int i; 01505 struct penalty_rule *pr_iter; 01506 01507 q->dead = 0; 01508 q->retry = DEFAULT_RETRY; 01509 q->timeout = DEFAULT_TIMEOUT; 01510 q->maxlen = 0; 01511 q->announcefrequency = 0; 01512 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY; 01513 q->announceholdtime = 1; 01514 q->announcepositionlimit = 10; /* Default 10 positions */ 01515 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */ 01516 q->roundingseconds = 0; /* Default - don't announce seconds */ 01517 q->servicelevel = 0; 01518 q->ringinuse = 1; 01519 q->setinterfacevar = 0; 01520 q->setqueuevar = 0; 01521 q->setqueueentryvar = 0; 01522 q->autofill = autofill_default; 01523 q->montype = montype_default; 01524 q->monfmt[0] = '\0'; 01525 q->reportholdtime = 0; 01526 q->wrapuptime = 0; 01527 q->penaltymemberslimit = 0; 01528 q->joinempty = 0; 01529 q->leavewhenempty = 0; 01530 q->memberdelay = 0; 01531 q->maskmemberstatus = 0; 01532 q->eventwhencalled = 0; 01533 q->weight = 0; 01534 q->timeoutrestart = 0; 01535 q->periodicannouncefrequency = 0; 01536 q->randomperiodicannounce = 0; 01537 q->numperiodicannounce = 0; 01538 q->autopause = QUEUE_AUTOPAUSE_OFF; 01539 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01540 if (!q->members) { 01541 if (q->strategy == QUEUE_STRATEGY_LINEAR) 01542 /* linear strategy depends on order, so we have to place all members in a single bucket */ 01543 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); 01544 else 01545 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 01546 } 01547 q->found = 1; 01548 01549 ast_string_field_set(q, sound_next, "queue-youarenext"); 01550 ast_string_field_set(q, sound_thereare, "queue-thereare"); 01551 ast_string_field_set(q, sound_calls, "queue-callswaiting"); 01552 ast_string_field_set(q, queue_quantity1, "queue-quantity1"); 01553 ast_string_field_set(q, queue_quantity2, "queue-quantity2"); 01554 ast_string_field_set(q, sound_holdtime, "queue-holdtime"); 01555 ast_string_field_set(q, sound_minutes, "queue-minutes"); 01556 ast_string_field_set(q, sound_minute, "queue-minute"); 01557 ast_string_field_set(q, sound_seconds, "queue-seconds"); 01558 ast_string_field_set(q, sound_thanks, "queue-thankyou"); 01559 ast_string_field_set(q, sound_reporthold, "queue-reporthold"); 01560 01561 if ((q->sound_periodicannounce[0] = ast_str_create(32))) 01562 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce"); 01563 01564 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 01565 if (q->sound_periodicannounce[i]) 01566 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", ""); 01567 } 01568 01569 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) 01570 ast_free(pr_iter); 01571 }
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 1157 of file app_queue.c.
References call_queue::head, queue_ent::next, and queue_ref().
Referenced by join_queue().
01158 { 01159 struct queue_ent *cur; 01160 01161 if (!q || !new) 01162 return; 01163 if (prev) { 01164 cur = prev->next; 01165 prev->next = new; 01166 } else { 01167 cur = q->head; 01168 q->head = new; 01169 } 01170 new->next = cur; 01171 01172 /* every queue_ent must have a reference to it's parent call_queue, this 01173 * reference does not go away until the end of the queue_ent's life, meaning 01174 * that even when the queue_ent leaves the call_queue this ref must remain. */ 01175 queue_ref(q); 01176 new->parent = q; 01177 new->pos = ++(*pos); 01178 new->opos = *pos; 01179 }
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 1602 of file app_queue.c.
References ast_calloc, ast_free, AST_LIST_INSERT_BEFORE_CURRENT, AST_LIST_INSERT_TAIL, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, ast_log(), ast_strdupa, ast_strlen_zero(), rule_list::list, LOG_WARNING, rule_list::name, and rule_list::rules.
Referenced by reload_queue_rules().
01603 { 01604 char *timestr, *maxstr, *minstr, *contentdup; 01605 struct penalty_rule *rule = NULL, *rule_iter; 01606 struct rule_list *rl_iter; 01607 int penaltychangetime, inserted = 0; 01608 01609 if (!(rule = ast_calloc(1, sizeof(*rule)))) { 01610 return -1; 01611 } 01612 01613 contentdup = ast_strdupa(content); 01614 01615 if (!(maxstr = strchr(contentdup, ','))) { 01616 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum); 01617 ast_free(rule); 01618 return -1; 01619 } 01620 01621 *maxstr++ = '\0'; 01622 timestr = contentdup; 01623 01624 if ((penaltychangetime = atoi(timestr)) < 0) { 01625 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum); 01626 ast_free(rule); 01627 return -1; 01628 } 01629 01630 rule->time = penaltychangetime; 01631 01632 if ((minstr = strchr(maxstr,','))) 01633 *minstr++ = '\0'; 01634 01635 /* The last check will evaluate true if either no penalty change is indicated for a given rule 01636 * OR if a min penalty change is indicated but no max penalty change is */ 01637 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') { 01638 rule->max_relative = 1; 01639 } 01640 01641 rule->max_value = atoi(maxstr); 01642 01643 if (!ast_strlen_zero(minstr)) { 01644 if (*minstr == '+' || *minstr == '-') 01645 rule->min_relative = 1; 01646 rule->min_value = atoi(minstr); 01647 } else /*there was no minimum specified, so assume this means no change*/ 01648 rule->min_relative = 1; 01649 01650 /*We have the rule made, now we need to insert it where it belongs*/ 01651 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){ 01652 if (strcasecmp(rl_iter->name, list_name)) 01653 continue; 01654 01655 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) { 01656 if (rule->time < rule_iter->time) { 01657 AST_LIST_INSERT_BEFORE_CURRENT(rule, list); 01658 inserted = 1; 01659 break; 01660 } 01661 } 01662 AST_LIST_TRAVERSE_SAFE_END; 01663 01664 if (!inserted) { 01665 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list); 01666 } 01667 } 01668 01669 return 0; 01670 }
static const char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 1049 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().
01050 { 01051 int x; 01052 01053 for (x = 0; x < ARRAY_LEN(strategies); x++) { 01054 if (strategy == strategies[x].strategy) 01055 return strategies[x].name; 01056 } 01057 01058 return "<unknown>"; 01059 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static] |
Definition at line 4912 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().
04913 { 04914 struct member *mem; 04915 struct ao2_iterator mem_iter; 04916 04917 if (!q) 04918 return NULL; 04919 04920 mem_iter = ao2_iterator_init(q->members, 0); 04921 while ((mem = ao2_iterator_next(&mem_iter))) { 04922 if (!strcasecmp(interface, mem->interface)) { 04923 ao2_iterator_destroy(&mem_iter); 04924 return mem; 04925 } 04926 ao2_ref(mem, -1); 04927 } 04928 ao2_iterator_destroy(&mem_iter); 04929 04930 return NULL; 04931 }
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 3681 of file app_queue.c.
References ao2_lock, ao2_unlock, ast_debug, call_queue::autofill, queue_ent::chan, call_queue::head, ast_channel::name, queue_ent::next, num_available_members(), queue_ent::parent, queue_ent::pending, and queue_ent::pos.
Referenced by queue_exec(), and wait_our_turn().
03682 { 03683 struct queue_ent *ch; 03684 int res; 03685 int avl; 03686 int idx = 0; 03687 /* This needs a lock. How many members are available to be served? */ 03688 ao2_lock(qe->parent); 03689 03690 avl = num_available_members(qe->parent); 03691 03692 ch = qe->parent->head; 03693 03694 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); 03695 03696 while ((idx < avl) && (ch) && (ch != qe)) { 03697 if (!ch->pending) 03698 idx++; 03699 ch = ch->next; 03700 } 03701 03702 ao2_unlock(qe->parent); 03703 /* If the queue entry is within avl [the number of available members] calls from the top ... 03704 * Autofill and position check added to support autofill=no (as only calls 03705 * from the front of the queue are valid when autofill is disabled) 03706 */ 03707 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) { 03708 ast_debug(1, "It's our turn (%s).\n", qe->chan->name); 03709 res = 1; 03710 } else { 03711 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); 03712 res = 0; 03713 } 03714 03715 return res; 03716 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason, | |||
int | position | |||
) | [static] |
Definition at line 2292 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, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, 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_channel::name, queue_ent::next, ast_party_id::number, queue_ent::pos, queue_ent::prio, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_UNKNOWN, queues, S_COR, status, ast_party_name::str, ast_party_number::str, ast_channel::uniqueid, ast_party_name::valid, and ast_party_number::valid.
Referenced by queue_exec().
02293 { 02294 struct call_queue *q; 02295 struct queue_ent *cur, *prev = NULL; 02296 int res = -1; 02297 int pos = 0; 02298 int inserted = 0; 02299 02300 if (!(q = load_realtime_queue(queuename))) 02301 return res; 02302 02303 ao2_lock(queues); 02304 ao2_lock(q); 02305 02306 /* This is our one */ 02307 if (q->joinempty) { 02308 int status = 0; 02309 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) { 02310 *reason = QUEUE_JOINEMPTY; 02311 ao2_unlock(q); 02312 ao2_unlock(queues); 02313 return res; 02314 } 02315 } 02316 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) 02317 *reason = QUEUE_FULL; 02318 else if (*reason == QUEUE_UNKNOWN) { 02319 /* There's space for us, put us at the right position inside 02320 * the queue. 02321 * Take into account the priority of the calling user */ 02322 inserted = 0; 02323 prev = NULL; 02324 cur = q->head; 02325 while (cur) { 02326 /* We have higher priority than the current user, enter 02327 * before him, after all the other users with priority 02328 * higher or equal to our priority. */ 02329 if ((!inserted) && (qe->prio > cur->prio)) { 02330 insert_entry(q, prev, qe, &pos); 02331 inserted = 1; 02332 } 02333 /* <= is necessary for the position comparison because it may not be possible to enter 02334 * at our desired position since higher-priority callers may have taken the position we want 02335 */ 02336 if (!inserted && (qe->prio >= cur->prio) && position && (position <= pos + 1)) { 02337 insert_entry(q, prev, qe, &pos); 02338 /*pos is incremented inside insert_entry, so don't need to add 1 here*/ 02339 if (position < pos) { 02340 ast_log(LOG_NOTICE, "Asked to be inserted at position %d but forced into position %d due to higher priority callers\n", position, pos); 02341 } 02342 inserted = 1; 02343 } 02344 cur->pos = ++pos; 02345 prev = cur; 02346 cur = cur->next; 02347 } 02348 /* No luck, join at the end of the queue */ 02349 if (!inserted) 02350 insert_entry(q, prev, qe, &pos); 02351 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 02352 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 02353 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 02354 q->count++; 02355 res = 0; 02356 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Join", 02357 "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n", 02358 qe->chan->name, 02359 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"),/* XXX somewhere else it is <unknown> */ 02360 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 02361 q->name, qe->pos, q->count, qe->chan->uniqueid ); 02362 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 02363 } 02364 ao2_unlock(q); 02365 ao2_unlock(queues); 02366 02367 return res; 02368 }
static int kill_dead_members | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 6437 of file app_queue.c.
References CMP_MATCH, member::delme, member::dynamic, get_queue_member_status(), call_queue::membercount, and member::status.
06438 { 06439 struct member *member = obj; 06440 struct call_queue *q = arg; 06441 06442 if (!member->delme) { 06443 if (member->dynamic) { 06444 /* dynamic members were not counted toward the member count 06445 * when reloading members from queues.conf, so we do that here 06446 */ 06447 q->membercount++; 06448 } 06449 member->status = get_queue_member_status(member); 06450 return 0; 06451 } else { 06452 q->membercount--; 06453 return CMP_MATCH; 06454 } 06455 }
static int kill_dead_queues | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 6575 of file app_queue.c.
References ast_strlen_zero(), CMP_MATCH, call_queue::dead, and call_queue::name.
Referenced by reload_queues().
06576 { 06577 struct call_queue *q = obj; 06578 char *queuename = arg; 06579 if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) { 06580 return CMP_MATCH; 06581 } else { 06582 return 0; 06583 } 06584 }
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 2595 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, penalty_rule::list, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, pbx_builtin_setvar_helper(), queue_ent::pos, queue_ent::qe_rules, queue_t_ref, queue_t_unref, queues, queues_t_unlink, call_queue::realtime, SENTINEL, ast_channel::uniqueid, and var.
Referenced by wait_our_turn().
02596 { 02597 struct call_queue *q; 02598 struct queue_ent *current, *prev = NULL; 02599 struct penalty_rule *pr_iter; 02600 int pos = 0; 02601 02602 if (!(q = qe->parent)) 02603 return; 02604 queue_t_ref(q, "Copy queue pointer from queue entry"); 02605 ao2_lock(q); 02606 02607 prev = NULL; 02608 for (current = q->head; current; current = current->next) { 02609 if (current == qe) { 02610 char posstr[20]; 02611 q->count--; 02612 02613 /* Take us out of the queue */ 02614 ast_manager_event(qe->chan, EVENT_FLAG_CALL, "Leave", 02615 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nPosition: %d\r\nUniqueid: %s\r\n", 02616 qe->chan->name, q->name, q->count, qe->pos, qe->chan->uniqueid); 02617 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 02618 /* Take us out of the queue */ 02619 if (prev) 02620 prev->next = current->next; 02621 else 02622 q->head = current->next; 02623 /* Free penalty rules */ 02624 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) 02625 ast_free(pr_iter); 02626 snprintf(posstr, sizeof(posstr), "%d", qe->pos); 02627 pbx_builtin_setvar_helper(qe->chan, "QUEUEPOSITION", posstr); 02628 } else { 02629 /* Renumber the people after us in the queue based on a new count */ 02630 current->pos = ++pos; 02631 prev = current; 02632 } 02633 } 02634 ao2_unlock(q); 02635 02636 /*If the queue is a realtime queue, check to see if it's still defined in real time*/ 02637 if (q->realtime) { 02638 struct ast_variable *var; 02639 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) { 02640 q->dead = 1; 02641 } else { 02642 ast_variables_destroy(var); 02643 } 02644 } 02645 02646 if (q->dead) { 02647 /* It's dead and nobody is in it, so kill it */ 02648 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container"); 02649 } 02650 /* unref the explicit ref earlier in the function */ 02651 queue_t_unref(q, "Expire copied reference"); 02652 }
static int load_module | ( | void | ) | [static] |
Definition at line 8125 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(), cli_queue, device_state_cb(), device_state_sub, devicestate_tps, 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_data_providers, queue_exec(), queue_hash_cb(), queueexists_function, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queuevar_function, queuewaitingcount_function, reload_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().
08126 { 08127 int res; 08128 struct ast_context *con; 08129 struct ast_flags mask = {AST_FLAGS_ALL, }; 08130 08131 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb); 08132 08133 use_weight = 0; 08134 08135 if (reload_handler(0, &mask, NULL)) 08136 return AST_MODULE_LOAD_DECLINE; 08137 08138 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue"); 08139 if (!con) 08140 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n"); 08141 else 08142 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue"); 08143 08144 if (queue_persistent_members) 08145 reload_queue_members(); 08146 08147 ast_data_register_multiple(queue_data_providers, ARRAY_LEN(queue_data_providers)); 08148 08149 ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue)); 08150 res = ast_register_application_xml(app, queue_exec); 08151 res |= ast_register_application_xml(app_aqm, aqm_exec); 08152 res |= ast_register_application_xml(app_rqm, rqm_exec); 08153 res |= ast_register_application_xml(app_pqm, pqm_exec); 08154 res |= ast_register_application_xml(app_upqm, upqm_exec); 08155 res |= ast_register_application_xml(app_ql, ql_exec); 08156 res |= ast_manager_register_xml("Queues", 0, manager_queues_show); 08157 res |= ast_manager_register_xml("QueueStatus", 0, manager_queues_status); 08158 res |= ast_manager_register_xml("QueueSummary", 0, manager_queues_summary); 08159 res |= ast_manager_register_xml("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member); 08160 res |= ast_manager_register_xml("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member); 08161 res |= ast_manager_register_xml("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member); 08162 res |= ast_manager_register_xml("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom); 08163 res |= ast_manager_register_xml("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty); 08164 res |= ast_manager_register_xml("QueueRule", 0, manager_queue_rule_show); 08165 res |= ast_manager_register_xml("QueueReload", 0, manager_queue_reload); 08166 res |= ast_manager_register_xml("QueueReset", 0, manager_queue_reset); 08167 res |= ast_custom_function_register(&queuevar_function); 08168 res |= ast_custom_function_register(&queueexists_function); 08169 res |= ast_custom_function_register(&queuemembercount_function); 08170 res |= ast_custom_function_register(&queuemembercount_dep); 08171 res |= ast_custom_function_register(&queuememberlist_function); 08172 res |= ast_custom_function_register(&queuewaitingcount_function); 08173 res |= ast_custom_function_register(&queuememberpenalty_function); 08174 08175 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) { 08176 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); 08177 } 08178 08179 /* in the following subscribe call, do I use DEVICE_STATE, or DEVICE_STATE_CHANGE? */ 08180 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, "AppQueue Device state", NULL, AST_EVENT_IE_END))) { 08181 res = -1; 08182 } 08183 08184 ast_extension_state_add(NULL, NULL, extension_state_cb, NULL); 08185 08186 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL); 08187 08188 return res ? AST_MODULE_LOAD_DECLINE : 0; 08189 }
static struct call_queue* load_realtime_queue | ( | const char * | queuename | ) | [static] |
Definition at line 2166 of file app_queue.c.
References ao2_lock, ao2_t_find, ao2_unlock, ast_atomic_fetchadd_int(), ast_config_destroy(), ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, OBJ_POINTER, queues, call_queue::realtime, SENTINEL, 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().
02167 { 02168 struct ast_variable *queue_vars; 02169 struct ast_config *member_config = NULL; 02170 struct call_queue *q = NULL, tmpq = { 02171 .name = queuename, 02172 }; 02173 int prev_weight = 0; 02174 02175 /* Find the queue in the in-core list first. */ 02176 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first"); 02177 02178 if (!q || q->realtime) { 02179 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all 02180 queue operations while waiting for the DB. 02181 02182 This will be two separate database transactions, so we might 02183 see queue parameters as they were before another process 02184 changed the queue and member list as it was after the change. 02185 Thus we might see an empty member list when a queue is 02186 deleted. In practise, this is unlikely to cause a problem. */ 02187 02188 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL); 02189 if (queue_vars) { 02190 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL); 02191 if (!member_config) { 02192 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n"); 02193 ast_variables_destroy(queue_vars); 02194 return NULL; 02195 } 02196 } 02197 if (q) { 02198 prev_weight = q->weight ? 1 : 0; 02199 } 02200 02201 ao2_lock(queues); 02202 02203 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 02204 if (member_config) { 02205 ast_config_destroy(member_config); 02206 } 02207 if (queue_vars) { 02208 ast_variables_destroy(queue_vars); 02209 } 02210 /* update the use_weight value if the queue's has gained or lost a weight */ 02211 if (q) { 02212 if (!q->weight && prev_weight) { 02213 ast_atomic_fetchadd_int(&use_weight, -1); 02214 } 02215 if (q->weight && !prev_weight) { 02216 ast_atomic_fetchadd_int(&use_weight, +1); 02217 } 02218 } 02219 /* Other cases will end up with the proper value for use_weight */ 02220 ao2_unlock(queues); 02221 02222 } else { 02223 update_realtime_members(q); 02224 } 02225 return q; 02226 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7100 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().
07101 { 07102 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface; 07103 int paused, penalty = 0; 07104 07105 queuename = astman_get_header(m, "Queue"); 07106 interface = astman_get_header(m, "Interface"); 07107 penalty_s = astman_get_header(m, "Penalty"); 07108 paused_s = astman_get_header(m, "Paused"); 07109 membername = astman_get_header(m, "MemberName"); 07110 state_interface = astman_get_header(m, "StateInterface"); 07111 07112 if (ast_strlen_zero(queuename)) { 07113 astman_send_error(s, m, "'Queue' not specified."); 07114 return 0; 07115 } 07116 07117 if (ast_strlen_zero(interface)) { 07118 astman_send_error(s, m, "'Interface' not specified."); 07119 return 0; 07120 } 07121 07122 if (ast_strlen_zero(penalty_s)) 07123 penalty = 0; 07124 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) 07125 penalty = 0; 07126 07127 if (ast_strlen_zero(paused_s)) 07128 paused = 0; 07129 else 07130 paused = abs(ast_true(paused_s)); 07131 07132 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) { 07133 case RES_OKAY: 07134 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 07135 astman_send_ack(s, m, "Added interface to queue"); 07136 break; 07137 case RES_EXISTS: 07138 astman_send_error(s, m, "Unable to add interface: Already there"); 07139 break; 07140 case RES_NOSUCHQUEUE: 07141 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 07142 break; 07143 case RES_OUTOFMEMORY: 07144 astman_send_error(s, m, "Out of memory"); 07145 break; 07146 } 07147 07148 return 0; 07149 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7185 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().
07186 { 07187 const char *queuename, *interface, *paused_s, *reason; 07188 int paused; 07189 07190 interface = astman_get_header(m, "Interface"); 07191 paused_s = astman_get_header(m, "Paused"); 07192 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 07193 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */ 07194 07195 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 07196 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 07197 return 0; 07198 } 07199 07200 paused = abs(ast_true(paused_s)); 07201 07202 if (set_member_paused(queuename, interface, reason, paused)) 07203 astman_send_error(s, m, "Interface not found"); 07204 else 07205 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 07206 return 0; 07207 }
static int manager_queue_log_custom | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7209 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().
07210 { 07211 const char *queuename, *event, *message, *interface, *uniqueid; 07212 07213 queuename = astman_get_header(m, "Queue"); 07214 uniqueid = astman_get_header(m, "UniqueId"); 07215 interface = astman_get_header(m, "Interface"); 07216 event = astman_get_header(m, "Event"); 07217 message = astman_get_header(m, "Message"); 07218 07219 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) { 07220 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters."); 07221 return 0; 07222 } 07223 07224 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message); 07225 astman_send_ack(s, m, "Event added successfully"); 07226 07227 return 0; 07228 }
static int manager_queue_member_penalty | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7308 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().
07309 { 07310 const char *queuename, *interface, *penalty_s; 07311 int penalty; 07312 07313 interface = astman_get_header(m, "Interface"); 07314 penalty_s = astman_get_header(m, "Penalty"); 07315 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ 07316 queuename = astman_get_header(m, "Queue"); 07317 07318 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { 07319 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); 07320 return 0; 07321 } 07322 07323 penalty = atoi(penalty_s); 07324 07325 if (set_member_penalty((char *)queuename, (char *)interface, penalty)) 07326 astman_send_error(s, m, "Invalid interface, queuename or penalty"); 07327 else 07328 astman_send_ack(s, m, "Interface penalty set successfully"); 07329 07330 return 0; 07331 }
static int manager_queue_reload | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7230 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().
07231 { 07232 struct ast_flags mask = {0,}; 07233 const char *queuename = NULL; 07234 int header_found = 0; 07235 07236 queuename = astman_get_header(m, "Queue"); 07237 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) { 07238 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 07239 header_found = 1; 07240 } 07241 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) { 07242 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 07243 header_found = 1; 07244 } 07245 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) { 07246 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 07247 header_found = 1; 07248 } 07249 07250 if (!header_found) { 07251 ast_set_flag(&mask, AST_FLAGS_ALL); 07252 } 07253 07254 if (!reload_handler(1, &mask, queuename)) { 07255 astman_send_ack(s, m, "Queue reloaded successfully"); 07256 } else { 07257 astman_send_error(s, m, "Error encountered while reloading queue"); 07258 } 07259 return 0; 07260 }
static int manager_queue_reset | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7262 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().
07263 { 07264 const char *queuename = NULL; 07265 struct ast_flags mask = {QUEUE_RESET_STATS,}; 07266 07267 queuename = astman_get_header(m, "Queue"); 07268 07269 if (!reload_handler(1, &mask, queuename)) { 07270 astman_send_ack(s, m, "Queue stats reset successfully"); 07271 } else { 07272 astman_send_error(s, m, "Error encountered while resetting queue stats"); 07273 } 07274 return 0; 07275 }
static int manager_queue_rule_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6903 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), penalty_rule::list, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, RESULT_SUCCESS, rule_list::rules, and penalty_rule::time.
Referenced by load_module().
06904 { 06905 const char *rule = astman_get_header(m, "Rule"); 06906 struct rule_list *rl_iter; 06907 struct penalty_rule *pr_iter; 06908 06909 AST_LIST_LOCK(&rule_lists); 06910 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 06911 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) { 06912 astman_append(s, "RuleList: %s\r\n", rl_iter->name); 06913 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 06914 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 ); 06915 } 06916 if (!ast_strlen_zero(rule)) 06917 break; 06918 } 06919 } 06920 AST_LIST_UNLOCK(&rule_lists); 06921 06922 astman_append(s, "\r\n\r\n"); 06923 06924 return RESULT_SUCCESS; 06925 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6893 of file app_queue.c.
References __queues_show(), astman_append(), and RESULT_SUCCESS.
Referenced by load_module().
06894 { 06895 static const char * const a[] = { "queue", "show" }; 06896 06897 __queues_show(s, -1, 2, a); 06898 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 06899 06900 return RESULT_SUCCESS; 06901 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Queue status info via AMI.
Definition at line 7003 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, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, ast_party_caller::id, int2strat(), member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, ast_party_id::name, ast_channel::name, call_queue::name, queue_ent::next, ast_party_id::number, member::paused, member::penalty, queue_t_unref, RESULT_SUCCESS, S_COR, call_queue::servicelevel, queue_ent::start, member::status, ast_party_name::str, ast_party_number::str, call_queue::strategy, call_queue::talktime, ast_channel::uniqueid, ast_party_name::valid, ast_party_number::valid, and call_queue::weight.
Referenced by load_module().
07004 { 07005 time_t now; 07006 int pos; 07007 const char *id = astman_get_header(m,"ActionID"); 07008 const char *queuefilter = astman_get_header(m,"Queue"); 07009 const char *memberfilter = astman_get_header(m,"Member"); 07010 char idText[256] = ""; 07011 struct call_queue *q; 07012 struct queue_ent *qe; 07013 float sl = 0; 07014 struct member *mem; 07015 struct ao2_iterator queue_iter; 07016 struct ao2_iterator mem_iter; 07017 07018 astman_send_ack(s, m, "Queue status will follow"); 07019 time(&now); 07020 if (!ast_strlen_zero(id)) 07021 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 07022 07023 queue_iter = ao2_iterator_init(queues, 0); 07024 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 07025 ao2_lock(q); 07026 07027 /* List queue properties */ 07028 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 07029 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 07030 astman_append(s, "Event: QueueParams\r\n" 07031 "Queue: %s\r\n" 07032 "Max: %d\r\n" 07033 "Strategy: %s\r\n" 07034 "Calls: %d\r\n" 07035 "Holdtime: %d\r\n" 07036 "TalkTime: %d\r\n" 07037 "Completed: %d\r\n" 07038 "Abandoned: %d\r\n" 07039 "ServiceLevel: %d\r\n" 07040 "ServicelevelPerf: %2.1f\r\n" 07041 "Weight: %d\r\n" 07042 "%s" 07043 "\r\n", 07044 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, 07045 q->callsabandoned, q->servicelevel, sl, q->weight, idText); 07046 /* List Queue Members */ 07047 mem_iter = ao2_iterator_init(q->members, 0); 07048 while ((mem = ao2_iterator_next(&mem_iter))) { 07049 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) { 07050 astman_append(s, "Event: QueueMember\r\n" 07051 "Queue: %s\r\n" 07052 "Name: %s\r\n" 07053 "Location: %s\r\n" 07054 "Membership: %s\r\n" 07055 "Penalty: %d\r\n" 07056 "CallsTaken: %d\r\n" 07057 "LastCall: %d\r\n" 07058 "Status: %d\r\n" 07059 "Paused: %d\r\n" 07060 "%s" 07061 "\r\n", 07062 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 07063 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 07064 } 07065 ao2_ref(mem, -1); 07066 } 07067 ao2_iterator_destroy(&mem_iter); 07068 /* List Queue Entries */ 07069 pos = 1; 07070 for (qe = q->head; qe; qe = qe->next) { 07071 astman_append(s, "Event: QueueEntry\r\n" 07072 "Queue: %s\r\n" 07073 "Position: %d\r\n" 07074 "Channel: %s\r\n" 07075 "Uniqueid: %s\r\n" 07076 "CallerIDNum: %s\r\n" 07077 "CallerIDName: %s\r\n" 07078 "Wait: %ld\r\n" 07079 "%s" 07080 "\r\n", 07081 q->name, pos++, qe->chan->name, qe->chan->uniqueid, 07082 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, "unknown"), 07083 S_COR(qe->chan->caller.id.name.valid, qe->chan->caller.id.name.str, "unknown"), 07084 (long) (now - qe->start), idText); 07085 } 07086 } 07087 ao2_unlock(q); 07088 queue_t_unref(q, "Done with iterator"); 07089 } 07090 ao2_iterator_destroy(&queue_iter); 07091 07092 astman_append(s, 07093 "Event: QueueStatusComplete\r\n" 07094 "%s" 07095 "\r\n",idText); 07096 07097 return RESULT_SUCCESS; 07098 }
static int manager_queues_summary | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Summary of queue info via the AMI.
Definition at line 6928 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), call_queue::head, call_queue::holdtime, call_queue::members, call_queue::name, queue_ent::next, member::paused, queue_t_unref, RESULT_SUCCESS, queue_ent::start, member::status, and call_queue::talktime.
Referenced by load_module().
06929 { 06930 time_t now; 06931 int qmemcount = 0; 06932 int qmemavail = 0; 06933 int qchancount = 0; 06934 int qlongestholdtime = 0; 06935 const char *id = astman_get_header(m, "ActionID"); 06936 const char *queuefilter = astman_get_header(m, "Queue"); 06937 char idText[256] = ""; 06938 struct call_queue *q; 06939 struct queue_ent *qe; 06940 struct member *mem; 06941 struct ao2_iterator queue_iter; 06942 struct ao2_iterator mem_iter; 06943 06944 astman_send_ack(s, m, "Queue summary will follow"); 06945 time(&now); 06946 if (!ast_strlen_zero(id)) 06947 snprintf(idText, 256, "ActionID: %s\r\n", id); 06948 queue_iter = ao2_iterator_init(queues, 0); 06949 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06950 ao2_lock(q); 06951 06952 /* List queue properties */ 06953 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 06954 /* Reset the necessary local variables if no queuefilter is set*/ 06955 qmemcount = 0; 06956 qmemavail = 0; 06957 qchancount = 0; 06958 qlongestholdtime = 0; 06959 06960 /* List Queue Members */ 06961 mem_iter = ao2_iterator_init(q->members, 0); 06962 while ((mem = ao2_iterator_next(&mem_iter))) { 06963 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) { 06964 ++qmemcount; 06965 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) { 06966 ++qmemavail; 06967 } 06968 } 06969 ao2_ref(mem, -1); 06970 } 06971 ao2_iterator_destroy(&mem_iter); 06972 for (qe = q->head; qe; qe = qe->next) { 06973 if ((now - qe->start) > qlongestholdtime) { 06974 qlongestholdtime = now - qe->start; 06975 } 06976 ++qchancount; 06977 } 06978 astman_append(s, "Event: QueueSummary\r\n" 06979 "Queue: %s\r\n" 06980 "LoggedIn: %d\r\n" 06981 "Available: %d\r\n" 06982 "Callers: %d\r\n" 06983 "HoldTime: %d\r\n" 06984 "TalkTime: %d\r\n" 06985 "LongestHoldTime: %d\r\n" 06986 "%s" 06987 "\r\n", 06988 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText); 06989 } 06990 ao2_unlock(q); 06991 queue_t_unref(q, "Done with iterator"); 06992 } 06993 ao2_iterator_destroy(&queue_iter); 06994 astman_append(s, 06995 "Event: QueueSummaryComplete\r\n" 06996 "%s" 06997 "\r\n", idText); 06998 06999 return RESULT_SUCCESS; 07000 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 7151 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().
07152 { 07153 const char *queuename, *interface; 07154 07155 queuename = astman_get_header(m, "Queue"); 07156 interface = astman_get_header(m, "Interface"); 07157 07158 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 07159 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 07160 return 0; 07161 } 07162 07163 switch (remove_from_queue(queuename, interface)) { 07164 case RES_OKAY: 07165 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 07166 astman_send_ack(s, m, "Removed interface from queue"); 07167 break; 07168 case RES_EXISTS: 07169 astman_send_error(s, m, "Unable to remove interface: Not there"); 07170 break; 07171 case RES_NOSUCHQUEUE: 07172 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 07173 break; 07174 case RES_OUTOFMEMORY: 07175 astman_send_error(s, m, "Out of memory"); 07176 break; 07177 case RES_NOT_DYNAMIC: 07178 astman_send_error(s, m, "Member not dynamic"); 07179 break; 07180 } 07181 07182 return 0; 07183 }
static int mark_dead_and_unfound | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 6564 of file app_queue.c.
References ast_strlen_zero(), call_queue::dead, call_queue::found, call_queue::name, and call_queue::realtime.
Referenced by reload_queues().
06565 { 06566 struct call_queue *q = obj; 06567 char *queuename = arg; 06568 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) { 06569 q->dead = 1; 06570 q->found = 0; 06571 } 06572 return 0; 06573 }
static int mark_member_dead | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 6428 of file app_queue.c.
References member::delme, and member::dynamic.
Referenced by reload_single_queue().
06429 { 06430 struct member *member = obj; 06431 if (!member->dynamic) { 06432 member->delme = 1; 06433 } 06434 return 0; 06435 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 1492 of file app_queue.c.
References CMP_MATCH, CMP_STOP, and member::interface.
Referenced by init_queue().
01493 { 01494 struct member *mem1 = obj1, *mem2 = obj2; 01495 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP; 01496 }
static int member_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1480 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
01481 { 01482 const struct member *mem = obj; 01483 const char *chname = strchr(mem->interface, '/'); 01484 int ret = 0, i; 01485 if (!chname) 01486 chname = mem->interface; 01487 for (i = 0; i < 5 && chname[i]; i++) 01488 ret += compress_char(chname[i]) << (i * 6); 01489 return ret; 01490 }
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 2700 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, call_queue::autofill, call_queue::members, member::paused, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.
Referenced by compare_weight(), and is_our_turn().
02701 { 02702 struct member *mem; 02703 int avl = 0; 02704 struct ao2_iterator mem_iter; 02705 02706 mem_iter = ao2_iterator_init(q->members, 0); 02707 while ((mem = ao2_iterator_next(&mem_iter))) { 02708 switch (mem->status) { 02709 case AST_DEVICE_INUSE: 02710 if (!q->ringinuse) 02711 break; 02712 /* else fall through */ 02713 case AST_DEVICE_NOT_INUSE: 02714 case AST_DEVICE_UNKNOWN: 02715 if (!mem->paused) { 02716 avl++; 02717 } 02718 break; 02719 } 02720 ao2_ref(mem, -1); 02721 02722 /* If autofill is not enabled or if the queue's strategy is ringall, then 02723 * we really don't care about the number of available members so much as we 02724 * do that there is at least one available. 02725 * 02726 * In fact, we purposely will return from this function stating that only 02727 * one member is available if either of those conditions hold. That way, 02728 * functions which determine what action to take based on the number of available 02729 * members will operate properly. The reasoning is that even if multiple 02730 * members are available, only the head caller can actually be serviced. 02731 */ 02732 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { 02733 break; 02734 } 02735 } 02736 ao2_iterator_destroy(&mem_iter); 02737 02738 return avl; 02739 }
static void parse_empty_options | ( | const char * | value, | |
enum empty_conditions * | empty, | |||
int | joinempty | |||
) | [static] |
Definition at line 1672 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, QUEUE_EMPTY_WRAPUP, and strsep().
Referenced by queue_set_param().
01673 { 01674 char *value_copy = ast_strdupa(value); 01675 char *option = NULL; 01676 while ((option = strsep(&value_copy, ","))) { 01677 if (!strcasecmp(option, "paused")) { 01678 *empty |= QUEUE_EMPTY_PAUSED; 01679 } else if (!strcasecmp(option, "penalty")) { 01680 *empty |= QUEUE_EMPTY_PENALTY; 01681 } else if (!strcasecmp(option, "inuse")) { 01682 *empty |= QUEUE_EMPTY_INUSE; 01683 } else if (!strcasecmp(option, "ringing")) { 01684 *empty |= QUEUE_EMPTY_RINGING; 01685 } else if (!strcasecmp(option, "invalid")) { 01686 *empty |= QUEUE_EMPTY_INVALID; 01687 } else if (!strcasecmp(option, "wrapup")) { 01688 *empty |= QUEUE_EMPTY_WRAPUP; 01689 } else if (!strcasecmp(option, "unavailable")) { 01690 *empty |= QUEUE_EMPTY_UNAVAILABLE; 01691 } else if (!strcasecmp(option, "unknown")) { 01692 *empty |= QUEUE_EMPTY_UNKNOWN; 01693 } else if (!strcasecmp(option, "loose")) { 01694 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID); 01695 } else if (!strcasecmp(option, "strict")) { 01696 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE); 01697 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) { 01698 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED); 01699 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) { 01700 *empty = 0; 01701 } else { 01702 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty"); 01703 } 01704 } 01705 }
static int play_file | ( | struct ast_channel * | chan, | |
const char * | filename | |||
) | [static] |
Definition at line 2370 of file app_queue.c.
References AST_DIGIT_ANY, ast_fileexists(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), queue_ent::chan, and ast_channel::language.
Referenced by say_periodic_announcement(), and say_position().
02371 { 02372 int res; 02373 02374 if (ast_strlen_zero(filename)) { 02375 return 0; 02376 } 02377 02378 if (!ast_fileexists(filename, NULL, chan->language)) { 02379 return 0; 02380 } 02381 02382 ast_stopstream(chan); 02383 02384 res = ast_streamfile(chan, filename, chan->language); 02385 if (!res) 02386 res = ast_waitstream(chan, AST_DIGIT_ANY); 02387 02388 ast_stopstream(chan); 02389 02390 return res; 02391 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
PauseQueueMember application.
Definition at line 5347 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().
05348 { 05349 char *parse; 05350 AST_DECLARE_APP_ARGS(args, 05351 AST_APP_ARG(queuename); 05352 AST_APP_ARG(interface); 05353 AST_APP_ARG(options); 05354 AST_APP_ARG(reason); 05355 ); 05356 05357 if (ast_strlen_zero(data)) { 05358 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n"); 05359 return -1; 05360 } 05361 05362 parse = ast_strdupa(data); 05363 05364 AST_STANDARD_APP_ARGS(args, parse); 05365 05366 if (ast_strlen_zero(args.interface)) { 05367 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 05368 return -1; 05369 } 05370 05371 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) { 05372 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 05373 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 05374 return 0; 05375 } 05376 05377 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 05378 05379 return 0; 05380 }
static int ql_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
QueueLog application.
Definition at line 5539 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().
05540 { 05541 char *parse; 05542 05543 AST_DECLARE_APP_ARGS(args, 05544 AST_APP_ARG(queuename); 05545 AST_APP_ARG(uniqueid); 05546 AST_APP_ARG(membername); 05547 AST_APP_ARG(event); 05548 AST_APP_ARG(params); 05549 ); 05550 05551 if (ast_strlen_zero(data)) { 05552 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n"); 05553 return -1; 05554 } 05555 05556 parse = ast_strdupa(data); 05557 05558 AST_STANDARD_APP_ARGS(args, parse); 05559 05560 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 05561 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 05562 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n"); 05563 return -1; 05564 } 05565 05566 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 05567 "%s", args.params ? args.params : ""); 05568 05569 return 0; 05570 }
static int queue_cmp_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 1100 of file app_queue.c.
References CMP_MATCH, CMP_STOP, and call_queue::name.
Referenced by load_module().
01101 { 01102 struct call_queue *q = obj, *q2 = arg; 01103 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0; 01104 }
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 5614 of file app_queue.c.
References call_queue::announcefrequency, args, AST_APP_ARG, 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_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::chan, copy_rules(), queue_ent::expire, get_member_status(), 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, call_queue::leavewhenempty, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::min_penalty, queue_ent::moh, call_queue::name, ast_channel::name, ast_party_id::number, queue_ent::opos, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_COR, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, status, stop, ast_party_number::str, penalty_rule::time, try_calling(), ast_channel::uniqueid, 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().
05615 { 05616 int res=-1; 05617 int ringing=0; 05618 const char *user_priority; 05619 const char *max_penalty_str; 05620 const char *min_penalty_str; 05621 int prio; 05622 int qcontinue = 0; 05623 int max_penalty, min_penalty; 05624 enum queue_result reason = QUEUE_UNKNOWN; 05625 /* whether to exit Queue application after the timeout hits */ 05626 int tries = 0; 05627 int noption = 0; 05628 char *parse; 05629 int makeannouncement = 0; 05630 int position = 0; 05631 AST_DECLARE_APP_ARGS(args, 05632 AST_APP_ARG(queuename); 05633 AST_APP_ARG(options); 05634 AST_APP_ARG(url); 05635 AST_APP_ARG(announceoverride); 05636 AST_APP_ARG(queuetimeoutstr); 05637 AST_APP_ARG(agi); 05638 AST_APP_ARG(macro); 05639 AST_APP_ARG(gosub); 05640 AST_APP_ARG(rule); 05641 AST_APP_ARG(position); 05642 ); 05643 /* Our queue entry */ 05644 struct queue_ent qe = { 0 }; 05645 05646 if (ast_strlen_zero(data)) { 05647 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule[,position]]]]]]]]]\n"); 05648 return -1; 05649 } 05650 05651 parse = ast_strdupa(data); 05652 AST_STANDARD_APP_ARGS(args, parse); 05653 05654 /* Setup our queue entry */ 05655 qe.start = time(NULL); 05656 05657 /* set the expire time based on the supplied timeout; */ 05658 if (!ast_strlen_zero(args.queuetimeoutstr)) 05659 qe.expire = qe.start + atoi(args.queuetimeoutstr); 05660 else 05661 qe.expire = 0; 05662 05663 /* Get the priority from the variable ${QUEUE_PRIO} */ 05664 ast_channel_lock(chan); 05665 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 05666 if (user_priority) { 05667 if (sscanf(user_priority, "%30d", &prio) == 1) { 05668 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio); 05669 } else { 05670 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 05671 user_priority, chan->name); 05672 prio = 0; 05673 } 05674 } else { 05675 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n"); 05676 prio = 0; 05677 } 05678 05679 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 05680 05681 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 05682 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) { 05683 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty); 05684 } else { 05685 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 05686 max_penalty_str, chan->name); 05687 max_penalty = 0; 05688 } 05689 } else { 05690 max_penalty = 0; 05691 } 05692 05693 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) { 05694 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) { 05695 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty); 05696 } else { 05697 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n", 05698 min_penalty_str, chan->name); 05699 min_penalty = 0; 05700 } 05701 } else { 05702 min_penalty = 0; 05703 } 05704 ast_channel_unlock(chan); 05705 05706 if (args.options && (strchr(args.options, 'r'))) 05707 ringing = 1; 05708 05709 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) { 05710 qe.ring_when_ringing = 1; 05711 } 05712 05713 if (args.options && (strchr(args.options, 'c'))) 05714 qcontinue = 1; 05715 05716 if (args.position) { 05717 position = atoi(args.position); 05718 if (position < 0) { 05719 ast_log(LOG_WARNING, "Invalid position '%s' given for call to queue '%s'. Assuming no preference for position\n", args.position, args.queuename); 05720 position = 0; 05721 } 05722 } 05723 05724 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 05725 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 05726 05727 qe.chan = chan; 05728 qe.prio = prio; 05729 qe.max_penalty = max_penalty; 05730 qe.min_penalty = min_penalty; 05731 qe.last_pos_said = 0; 05732 qe.last_pos = 0; 05733 qe.last_periodic_announce_time = time(NULL); 05734 qe.last_periodic_announce_sound = 0; 05735 qe.valid_digits = 0; 05736 if (join_queue(args.queuename, &qe, &reason, position)) { 05737 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 05738 set_queue_result(chan, reason); 05739 return 0; 05740 } 05741 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s|%d", 05742 S_OR(args.url, ""), 05743 S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, ""), 05744 qe.opos); 05745 copy_rules(&qe, args.rule); 05746 qe.pr = AST_LIST_FIRST(&qe.qe_rules); 05747 check_turns: 05748 if (ringing) { 05749 ast_indicate(chan, AST_CONTROL_RINGING); 05750 } else { 05751 ast_moh_start(chan, qe.moh, NULL); 05752 } 05753 05754 /* This is the wait loop for callers 2 through maxlen */ 05755 res = wait_our_turn(&qe, ringing, &reason); 05756 if (res) { 05757 goto stop; 05758 } 05759 05760 makeannouncement = 0; 05761 05762 for (;;) { 05763 /* This is the wait loop for the head caller*/ 05764 /* To exit, they may get their call answered; */ 05765 /* they may dial a digit from the queue context; */ 05766 /* or, they may timeout. */ 05767 05768 /* Leave if we have exceeded our queuetimeout */ 05769 if (qe.expire && (time(NULL) >= qe.expire)) { 05770 record_abandoned(&qe); 05771 reason = QUEUE_TIMEOUT; 05772 res = 0; 05773 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 05774 qe.pos, qe.opos, (long) time(NULL) - qe.start); 05775 break; 05776 } 05777 05778 if (makeannouncement) { 05779 /* Make a position announcement, if enabled */ 05780 if (qe.parent->announcefrequency) 05781 if ((res = say_position(&qe,ringing))) 05782 goto stop; 05783 } 05784 makeannouncement = 1; 05785 05786 /* Make a periodic announcement, if enabled */ 05787 if (qe.parent->periodicannouncefrequency) 05788 if ((res = say_periodic_announcement(&qe,ringing))) 05789 goto stop; 05790 05791 /* Leave if we have exceeded our queuetimeout */ 05792 if (qe.expire && (time(NULL) >= qe.expire)) { 05793 record_abandoned(&qe); 05794 reason = QUEUE_TIMEOUT; 05795 res = 0; 05796 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 05797 break; 05798 } 05799 05800 /* see if we need to move to the next penalty level for this queue */ 05801 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) { 05802 update_qe_rule(&qe); 05803 } 05804 05805 /* Try calling all queue members for 'timeout' seconds */ 05806 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing); 05807 if (res) { 05808 goto stop; 05809 } 05810 05811 if (qe.parent->leavewhenempty) { 05812 int status = 0; 05813 if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) { 05814 record_abandoned(&qe); 05815 reason = QUEUE_LEAVEEMPTY; 05816 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 05817 res = 0; 05818 break; 05819 } 05820 } 05821 05822 /* exit after 'timeout' cycle if 'n' option enabled */ 05823 if (noption && tries >= qe.parent->membercount) { 05824 ast_verb(3, "Exiting on time-out cycle\n"); 05825 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 05826 record_abandoned(&qe); 05827 reason = QUEUE_TIMEOUT; 05828 res = 0; 05829 break; 05830 } 05831 05832 05833 /* Leave if we have exceeded our queuetimeout */ 05834 if (qe.expire && (time(NULL) >= qe.expire)) { 05835 record_abandoned(&qe); 05836 reason = QUEUE_TIMEOUT; 05837 res = 0; 05838 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start); 05839 break; 05840 } 05841 05842 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 05843 update_realtime_members(qe.parent); 05844 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 05845 res = wait_a_bit(&qe); 05846 if (res) 05847 goto stop; 05848 05849 /* Since this is a priority queue and 05850 * it is not sure that we are still at the head 05851 * of the queue, go and check for our turn again. 05852 */ 05853 if (!is_our_turn(&qe)) { 05854 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name); 05855 goto check_turns; 05856 } 05857 } 05858 05859 stop: 05860 if (res) { 05861 if (res < 0) { 05862 if (!qe.handled) { 05863 record_abandoned(&qe); 05864 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 05865 "%d|%d|%ld", qe.pos, qe.opos, 05866 (long) time(NULL) - qe.start); 05867 res = -1; 05868 } else if (qcontinue) { 05869 reason = QUEUE_CONTINUE; 05870 res = 0; 05871 } 05872 } else if (qe.valid_digits) { 05873 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 05874 "%s|%d", qe.digits, qe.pos); 05875 } 05876 } 05877 05878 /* Don't allow return code > 0 */ 05879 if (res >= 0) { 05880 res = 0; 05881 if (ringing) { 05882 ast_indicate(chan, -1); 05883 } else { 05884 ast_moh_stop(chan); 05885 } 05886 ast_stopstream(chan); 05887 } 05888 05889 set_queue_variables(qe.parent, qe.chan); 05890 05891 leave_queue(&qe); 05892 if (reason != QUEUE_UNKNOWN) 05893 set_queue_result(chan, reason); 05894 05895 if (qe.parent) { 05896 /* every queue_ent is given a reference to it's parent call_queue when it joins the queue. 05897 * This ref must be taken away right before the queue_ent is destroyed. In this case 05898 * the queue_ent is about to be returned on the stack */ 05899 qe.parent = queue_unref(qe.parent); 05900 } 05901 05902 return res; 05903 }
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 5957 of file app_queue.c.
References ast_log(), ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, and queue_t_unref.
05958 { 05959 struct call_queue *q; 05960 05961 buf[0] = '\0'; 05962 05963 if (ast_strlen_zero(data)) { 05964 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05965 return -1; 05966 } 05967 q = load_realtime_queue(data); 05968 snprintf(buf, len, "%d", q != NULL? 1 : 0); 05969 if (q) { 05970 queue_t_unref(q, "Done with temporary reference in QUEUE_EXISTS()"); 05971 } 05972 05973 return 0; 05974 }
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 6175 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.
06176 { 06177 int penalty; 06178 AST_DECLARE_APP_ARGS(args, 06179 AST_APP_ARG(queuename); 06180 AST_APP_ARG(interface); 06181 ); 06182 /* Make sure the returned value on error is NULL. */ 06183 buf[0] = '\0'; 06184 06185 if (ast_strlen_zero(data)) { 06186 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06187 return -1; 06188 } 06189 06190 AST_STANDARD_APP_ARGS(args, data); 06191 06192 if (args.argc < 2) { 06193 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06194 return -1; 06195 } 06196 06197 penalty = get_member_penalty (args.queuename, args.interface); 06198 06199 if (penalty >= 0) /* remember that buf is already '\0' */ 06200 snprintf (buf, len, "%d", penalty); 06201 06202 return 0; 06203 }
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 6206 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().
06207 { 06208 int penalty; 06209 AST_DECLARE_APP_ARGS(args, 06210 AST_APP_ARG(queuename); 06211 AST_APP_ARG(interface); 06212 ); 06213 06214 if (ast_strlen_zero(data)) { 06215 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06216 return -1; 06217 } 06218 06219 AST_STANDARD_APP_ARGS(args, data); 06220 06221 if (args.argc < 2) { 06222 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 06223 return -1; 06224 } 06225 06226 penalty = atoi(value); 06227 06228 if (ast_strlen_zero(args.interface)) { 06229 ast_log (LOG_ERROR, "<interface> parameter can't be null\n"); 06230 return -1; 06231 } 06232 06233 /* if queuename = NULL then penalty will be set for interface in all the queues. */ 06234 if (set_member_penalty(args.queuename, args.interface, penalty)) { 06235 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n"); 06236 return -1; 06237 } 06238 06239 return 0; 06240 }
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 5981 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlock, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, member::lastcall, load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_t_unref, and member::status.
05982 { 05983 int count = 0; 05984 struct member *m; 05985 struct ao2_iterator mem_iter; 05986 struct call_queue *q; 05987 char *option; 05988 05989 if (ast_strlen_zero(data)) { 05990 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05991 return -1; 05992 } 05993 05994 if ((option = strchr(data, ','))) 05995 *option++ = '\0'; 05996 else 05997 option = "logged"; 05998 if ((q = load_realtime_queue(data))) { 05999 ao2_lock(q); 06000 if (!strcasecmp(option, "logged")) { 06001 mem_iter = ao2_iterator_init(q->members, 0); 06002 while ((m = ao2_iterator_next(&mem_iter))) { 06003 /* Count the agents who are logged in and presently answering calls */ 06004 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 06005 count++; 06006 } 06007 ao2_ref(m, -1); 06008 } 06009 ao2_iterator_destroy(&mem_iter); 06010 } else if (!strcasecmp(option, "free")) { 06011 mem_iter = ao2_iterator_init(q->members, 0); 06012 while ((m = ao2_iterator_next(&mem_iter))) { 06013 /* Count the agents who are logged in and presently answering calls */ 06014 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) { 06015 count++; 06016 } 06017 ao2_ref(m, -1); 06018 } 06019 ao2_iterator_destroy(&mem_iter); 06020 } else if (!strcasecmp(option, "ready")) { 06021 time_t now; 06022 time(&now); 06023 mem_iter = ao2_iterator_init(q->members, 0); 06024 while ((m = ao2_iterator_next(&mem_iter))) { 06025 /* Count the agents who are logged in, not paused and not wrapping up */ 06026 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused) && 06027 !(m->lastcall && q->wrapuptime && ((now - q->wrapuptime) < m->lastcall))) { 06028 count++; 06029 } 06030 ao2_ref(m, -1); 06031 } 06032 ao2_iterator_destroy(&mem_iter); 06033 } else /* must be "count" */ 06034 count = q->membercount; 06035 ao2_unlock(q); 06036 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()"); 06037 } else 06038 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06039 06040 snprintf(buf, len, "%d", count); 06041 06042 return 0; 06043 }
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 6050 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, queue_t_unref, and member::status.
06051 { 06052 int count = 0; 06053 struct member *m; 06054 struct call_queue *q; 06055 struct ao2_iterator mem_iter; 06056 static int depflag = 1; 06057 06058 if (depflag) { 06059 depflag = 0; 06060 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"); 06061 } 06062 06063 if (ast_strlen_zero(data)) { 06064 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 06065 return -1; 06066 } 06067 06068 if ((q = load_realtime_queue(data))) { 06069 ao2_lock(q); 06070 mem_iter = ao2_iterator_init(q->members, 0); 06071 while ((m = ao2_iterator_next(&mem_iter))) { 06072 /* Count the agents who are logged in and presently answering calls */ 06073 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 06074 count++; 06075 } 06076 ao2_ref(m, -1); 06077 } 06078 ao2_iterator_destroy(&mem_iter); 06079 ao2_unlock(q); 06080 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT"); 06081 } else 06082 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06083 06084 snprintf(buf, len, "%d", count); 06085 06086 return 0; 06087 }
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 6126 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, and queue_t_unref.
06127 { 06128 struct call_queue *q, tmpq = { 06129 .name = data, 06130 }; 06131 struct member *m; 06132 06133 /* Ensure an otherwise empty list doesn't return garbage */ 06134 buf[0] = '\0'; 06135 06136 if (ast_strlen_zero(data)) { 06137 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 06138 return -1; 06139 } 06140 06141 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) { 06142 int buflen = 0, count = 0; 06143 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 06144 06145 ao2_lock(q); 06146 while ((m = ao2_iterator_next(&mem_iter))) { 06147 /* strcat() is always faster than printf() */ 06148 if (count++) { 06149 strncat(buf + buflen, ",", len - buflen - 1); 06150 buflen++; 06151 } 06152 strncat(buf + buflen, m->interface, len - buflen - 1); 06153 buflen += strlen(m->interface); 06154 /* Safeguard against overflow (negative length) */ 06155 if (buflen >= len - 2) { 06156 ao2_ref(m, -1); 06157 ast_log(LOG_WARNING, "Truncating list\n"); 06158 break; 06159 } 06160 ao2_ref(m, -1); 06161 } 06162 ao2_iterator_destroy(&mem_iter); 06163 ao2_unlock(q); 06164 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()"); 06165 } else 06166 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06167 06168 /* We should already be terminated, but let's make sure. */ 06169 buf[len - 1] = '\0'; 06170 06171 return 0; 06172 }
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 6090 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, SENTINEL, and var.
06091 { 06092 int count = 0; 06093 struct call_queue *q, tmpq = { 06094 .name = data, 06095 }; 06096 struct ast_variable *var = NULL; 06097 06098 buf[0] = '\0'; 06099 06100 if (ast_strlen_zero(data)) { 06101 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n"); 06102 return -1; 06103 } 06104 06105 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) { 06106 ao2_lock(q); 06107 count = q->count; 06108 ao2_unlock(q); 06109 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()"); 06110 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) { 06111 /* if the queue is realtime but was not found in memory, this 06112 * means that the queue had been deleted from memory since it was 06113 * "dead." This means it has a 0 waiting count 06114 */ 06115 count = 0; 06116 ast_variables_destroy(var); 06117 } else 06118 ast_log(LOG_WARNING, "queue %s was not found\n", data); 06119 06120 snprintf(buf, len, "%d", count); 06121 06122 return 0; 06123 }
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 5910 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, call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.
05911 { 05912 int res = -1; 05913 struct call_queue *q, tmpq = { 05914 .name = data, 05915 }; 05916 05917 char interfacevar[256] = ""; 05918 float sl = 0; 05919 05920 if (ast_strlen_zero(data)) { 05921 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05922 return -1; 05923 } 05924 05925 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) { 05926 ao2_lock(q); 05927 if (q->setqueuevar) { 05928 sl = 0; 05929 res = 0; 05930 05931 if (q->callscompleted > 0) { 05932 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 05933 } 05934 05935 snprintf(interfacevar, sizeof(interfacevar), 05936 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 05937 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 05938 05939 pbx_builtin_setvar_multiple(chan, interfacevar); 05940 } 05941 05942 ao2_unlock(q); 05943 queue_t_unref(q, "Done with QUEUE() function"); 05944 } else { 05945 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05946 } 05947 05948 snprintf(buf, len, "%d", res); 05949 05950 return 0; 05951 }
static int queue_hash_cb | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 1093 of file app_queue.c.
References ast_str_case_hash(), and call_queue::name.
Referenced by load_module().
01094 { 01095 const struct call_queue *q = obj; 01096 01097 return ast_str_case_hash(q->name); 01098 }
static struct call_queue* queue_ref | ( | struct call_queue * | q | ) | [inline, static] |
Definition at line 1118 of file app_queue.c.
References ao2_ref.
Referenced by insert_entry().
01119 { 01120 ao2_ref(q, 1); 01121 return q; 01122 }
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 6332 of file app_queue.c.
References ast_true(), and ast_variable_retrieve().
Referenced by reload_queues().
06333 { 06334 const char *general_val = NULL; 06335 queue_persistent_members = 0; 06336 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 06337 queue_persistent_members = ast_true(general_val); 06338 autofill_default = 0; 06339 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 06340 autofill_default = ast_true(general_val); 06341 montype_default = 0; 06342 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) { 06343 if (!strcasecmp(general_val, "mixmonitor")) 06344 montype_default = 1; 06345 } 06346 update_cdr = 0; 06347 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) 06348 update_cdr = ast_true(general_val); 06349 shared_lastcall = 0; 06350 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) 06351 shared_lastcall = ast_true(general_val); 06352 }
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 1715 of file app_queue.c.
References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, autopause2int(), queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::numperiodicannounce, parse_empty_options(), call_queue::penaltymemberslimit, call_queue::periodicannouncefrequency, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, call_queue::randomperiodicannounce, call_queue::relativeperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by reload_single_queue().
01716 { 01717 if (!strcasecmp(param, "musicclass") || 01718 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 01719 ast_string_field_set(q, moh, val); 01720 } else if (!strcasecmp(param, "announce")) { 01721 ast_string_field_set(q, announce, val); 01722 } else if (!strcasecmp(param, "context")) { 01723 ast_string_field_set(q, context, val); 01724 } else if (!strcasecmp(param, "timeout")) { 01725 q->timeout = atoi(val); 01726 if (q->timeout < 0) 01727 q->timeout = DEFAULT_TIMEOUT; 01728 } else if (!strcasecmp(param, "ringinuse")) { 01729 q->ringinuse = ast_true(val); 01730 } else if (!strcasecmp(param, "setinterfacevar")) { 01731 q->setinterfacevar = ast_true(val); 01732 } else if (!strcasecmp(param, "setqueuevar")) { 01733 q->setqueuevar = ast_true(val); 01734 } else if (!strcasecmp(param, "setqueueentryvar")) { 01735 q->setqueueentryvar = ast_true(val); 01736 } else if (!strcasecmp(param, "monitor-format")) { 01737 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 01738 } else if (!strcasecmp(param, "membermacro")) { 01739 ast_string_field_set(q, membermacro, val); 01740 } else if (!strcasecmp(param, "membergosub")) { 01741 ast_string_field_set(q, membergosub, val); 01742 } else if (!strcasecmp(param, "queue-youarenext")) { 01743 ast_string_field_set(q, sound_next, val); 01744 } else if (!strcasecmp(param, "queue-thereare")) { 01745 ast_string_field_set(q, sound_thereare, val); 01746 } else if (!strcasecmp(param, "queue-callswaiting")) { 01747 ast_string_field_set(q, sound_calls, val); 01748 } else if (!strcasecmp(param, "queue-quantity1")) { 01749 ast_string_field_set(q, queue_quantity1, val); 01750 } else if (!strcasecmp(param, "queue-quantity2")) { 01751 ast_string_field_set(q, queue_quantity2, val); 01752 } else if (!strcasecmp(param, "queue-holdtime")) { 01753 ast_string_field_set(q, sound_holdtime, val); 01754 } else if (!strcasecmp(param, "queue-minutes")) { 01755 ast_string_field_set(q, sound_minutes, val); 01756 } else if (!strcasecmp(param, "queue-minute")) { 01757 ast_string_field_set(q, sound_minute, val); 01758 } else if (!strcasecmp(param, "queue-seconds")) { 01759 ast_string_field_set(q, sound_seconds, val); 01760 } else if (!strcasecmp(param, "queue-thankyou")) { 01761 ast_string_field_set(q, sound_thanks, val); 01762 } else if (!strcasecmp(param, "queue-callerannounce")) { 01763 ast_string_field_set(q, sound_callerannounce, val); 01764 } else if (!strcasecmp(param, "queue-reporthold")) { 01765 ast_string_field_set(q, sound_reporthold, val); 01766 } else if (!strcasecmp(param, "announce-frequency")) { 01767 q->announcefrequency = atoi(val); 01768 } else if (!strcasecmp(param, "min-announce-frequency")) { 01769 q->minannouncefrequency = atoi(val); 01770 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name); 01771 } else if (!strcasecmp(param, "announce-round-seconds")) { 01772 q->roundingseconds = atoi(val); 01773 /* Rounding to any other values just doesn't make sense... */ 01774 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10 01775 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) { 01776 if (linenum >= 0) { 01777 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01778 "using 0 instead for queue '%s' at line %d of queues.conf\n", 01779 val, param, q->name, linenum); 01780 } else { 01781 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01782 "using 0 instead for queue '%s'\n", val, param, q->name); 01783 } 01784 q->roundingseconds=0; 01785 } 01786 } else if (!strcasecmp(param, "announce-holdtime")) { 01787 if (!strcasecmp(val, "once")) 01788 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 01789 else if (ast_true(val)) 01790 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 01791 else 01792 q->announceholdtime = 0; 01793 } else if (!strcasecmp(param, "announce-position")) { 01794 if (!strcasecmp(val, "limit")) 01795 q->announceposition = ANNOUNCEPOSITION_LIMIT; 01796 else if (!strcasecmp(val, "more")) 01797 q->announceposition = ANNOUNCEPOSITION_MORE_THAN; 01798 else if (ast_true(val)) 01799 q->announceposition = ANNOUNCEPOSITION_YES; 01800 else 01801 q->announceposition = ANNOUNCEPOSITION_NO; 01802 } else if (!strcasecmp(param, "announce-position-limit")) { 01803 q->announcepositionlimit = atoi(val); 01804 } else if (!strcasecmp(param, "periodic-announce")) { 01805 if (strchr(val, ',')) { 01806 char *s, *buf = ast_strdupa(val); 01807 unsigned int i = 0; 01808 01809 while ((s = strsep(&buf, ",|"))) { 01810 if (!q->sound_periodicannounce[i]) 01811 q->sound_periodicannounce[i] = ast_str_create(16); 01812 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s); 01813 i++; 01814 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 01815 break; 01816 } 01817 q->numperiodicannounce = i; 01818 } else { 01819 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val); 01820 q->numperiodicannounce = 1; 01821 } 01822 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 01823 q->periodicannouncefrequency = atoi(val); 01824 } else if (!strcasecmp(param, "relative-periodic-announce")) { 01825 q->relativeperiodicannounce = ast_true(val); 01826 } else if (!strcasecmp(param, "random-periodic-announce")) { 01827 q->randomperiodicannounce = ast_true(val); 01828 } else if (!strcasecmp(param, "retry")) { 01829 q->retry = atoi(val); 01830 if (q->retry <= 0) 01831 q->retry = DEFAULT_RETRY; 01832 } else if (!strcasecmp(param, "wrapuptime")) { 01833 q->wrapuptime = atoi(val); 01834 } else if (!strcasecmp(param, "penaltymemberslimit")) { 01835 if ((sscanf(val, "%10d", &q->penaltymemberslimit) != 1)) { 01836 q->penaltymemberslimit = 0; 01837 } 01838 } else if (!strcasecmp(param, "autofill")) { 01839 q->autofill = ast_true(val); 01840 } else if (!strcasecmp(param, "monitor-type")) { 01841 if (!strcasecmp(val, "mixmonitor")) 01842 q->montype = 1; 01843 } else if (!strcasecmp(param, "autopause")) { 01844 q->autopause = autopause2int(val); 01845 } else if (!strcasecmp(param, "maxlen")) { 01846 q->maxlen = atoi(val); 01847 if (q->maxlen < 0) 01848 q->maxlen = 0; 01849 } else if (!strcasecmp(param, "servicelevel")) { 01850 q->servicelevel= atoi(val); 01851 } else if (!strcasecmp(param, "strategy")) { 01852 int strategy; 01853 01854 /* We are a static queue and already have set this, no need to do it again */ 01855 if (failunknown) { 01856 return; 01857 } 01858 strategy = strat2int(val); 01859 if (strategy < 0) { 01860 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01861 val, q->name); 01862 q->strategy = QUEUE_STRATEGY_RINGALL; 01863 } 01864 if (strategy == q->strategy) { 01865 return; 01866 } 01867 if (strategy == QUEUE_STRATEGY_LINEAR) { 01868 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n"); 01869 return; 01870 } 01871 q->strategy = strategy; 01872 } else if (!strcasecmp(param, "joinempty")) { 01873 parse_empty_options(val, &q->joinempty, 1); 01874 } else if (!strcasecmp(param, "leavewhenempty")) { 01875 parse_empty_options(val, &q->leavewhenempty, 0); 01876 } else if (!strcasecmp(param, "eventmemberstatus")) { 01877 q->maskmemberstatus = !ast_true(val); 01878 } else if (!strcasecmp(param, "eventwhencalled")) { 01879 if (!strcasecmp(val, "vars")) { 01880 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 01881 } else { 01882 q->eventwhencalled = ast_true(val) ? 1 : 0; 01883 } 01884 } else if (!strcasecmp(param, "reportholdtime")) { 01885 q->reportholdtime = ast_true(val); 01886 } else if (!strcasecmp(param, "memberdelay")) { 01887 q->memberdelay = atoi(val); 01888 } else if (!strcasecmp(param, "weight")) { 01889 q->weight = atoi(val); 01890 } else if (!strcasecmp(param, "timeoutrestart")) { 01891 q->timeoutrestart = ast_true(val); 01892 } else if (!strcasecmp(param, "defaultrule")) { 01893 ast_string_field_set(q, defaultrule, val); 01894 } else if (!strcasecmp(param, "timeoutpriority")) { 01895 if (!strcasecmp(val, "conf")) { 01896 q->timeoutpriority = TIMEOUT_PRIORITY_CONF; 01897 } else { 01898 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01899 } 01900 } else if (failunknown) { 01901 if (linenum >= 0) { 01902 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 01903 q->name, param, linenum); 01904 } else { 01905 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 01906 } 01907 } 01908 }
static char* queue_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6874 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.
06875 { 06876 switch ( cmd ) { 06877 case CLI_INIT: 06878 e->command = "queue show"; 06879 e->usage = 06880 "Usage: queue show\n" 06881 " Provides summary information on a specified queue.\n"; 06882 return NULL; 06883 case CLI_GENERATE: 06884 return complete_queue_show(a->line, a->word, a->pos, a->n); 06885 } 06886 06887 return __queues_show(NULL, a->fd, a->argc, a->argv); 06888 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 3997 of file app_queue.c.
References ast_free.
03998 { 03999 struct queue_transfer_ds *qtds = data; 04000 ast_free(qtds); 04001 }
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 4020 of file app_queue.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::opos, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, ast_channel::uniqueid, and update_queue().
04021 { 04022 struct queue_transfer_ds *qtds = data; 04023 struct queue_ent *qe = qtds->qe; 04024 struct member *member = qtds->member; 04025 time_t callstart = qtds->starttime; 04026 int callcompletedinsl = qtds->callcompletedinsl; 04027 struct ast_datastore *datastore; 04028 04029 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 04030 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 04031 (long) (time(NULL) - callstart), qe->opos); 04032 04033 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 04034 04035 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 04036 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 04037 ast_channel_datastore_remove(old_chan, datastore); 04038 } else { 04039 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 04040 } 04041 }
static struct call_queue* queue_unref | ( | struct call_queue * | q | ) | [inline, static] |
Definition at line 1124 of file app_queue.c.
References ao2_ref.
Referenced by queues_data_provider_get().
01125 { 01126 ao2_ref(q, -1); 01127 return q; 01128 }
static int queues_data_provider_get | ( | const struct ast_data_search * | search, | |
struct ast_data * | data_root | |||
) | [static] |
Definition at line 8020 of file app_queue.c.
References 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_data_provider_get_helper(), and SENTINEL.
08022 { 08023 struct ao2_iterator i; 08024 struct call_queue *queue, *queue_realtime = NULL; 08025 struct ast_config *cfg; 08026 char *queuename; 08027 08028 /* load realtime queues. */ 08029 cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 08030 if (cfg) { 08031 for (queuename = ast_category_browse(cfg, NULL); 08032 !ast_strlen_zero(queuename); 08033 queuename = ast_category_browse(cfg, queuename)) { 08034 if ((queue = load_realtime_queue(queuename))) { 08035 queue_unref(queue); 08036 } 08037 } 08038 ast_config_destroy(cfg); 08039 } 08040 08041 /* static queues. */ 08042 i = ao2_iterator_init(queues, 0); 08043 while ((queue = ao2_iterator_next(&i))) { 08044 ao2_lock(queue); 08045 if (queue->realtime && !(queue_realtime = load_realtime_queue(queue->name))) { 08046 ao2_unlock(queue); 08047 queue_unref(queue); 08048 continue; 08049 } else if (queue->realtime) { 08050 queue_unref(queue_realtime); 08051 } 08052 08053 queues_data_provider_get_helper(search, data_root, queue); 08054 ao2_unlock(queue); 08055 queue_unref(queue); 08056 } 08057 08058 return 0; 08059 }
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 7916 of file app_queue.c.
References call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, 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, queue_ent::next, and call_queue::strategy.
Referenced by queues_data_provider_get().
07918 { 07919 struct ao2_iterator im; 07920 struct member *member; 07921 struct queue_ent *qe; 07922 struct ast_data *data_queue, *data_members = NULL, *enum_node; 07923 struct ast_data *data_member, *data_callers = NULL, *data_caller, *data_caller_channel; 07924 07925 data_queue = ast_data_add_node(data_root, "queue"); 07926 if (!data_queue) { 07927 return; 07928 } 07929 07930 ast_data_add_structure(call_queue, data_queue, queue); 07931 07932 ast_data_add_str(data_queue, "strategy", int2strat(queue->strategy)); 07933 07934 /* announce position */ 07935 enum_node = ast_data_add_node(data_queue, "announceposition"); 07936 if (!enum_node) { 07937 return; 07938 } 07939 switch (queue->announceposition) { 07940 case ANNOUNCEPOSITION_LIMIT: 07941 ast_data_add_str(enum_node, "text", "limit"); 07942 break; 07943 case ANNOUNCEPOSITION_MORE_THAN: 07944 ast_data_add_str(enum_node, "text", "more"); 07945 break; 07946 case ANNOUNCEPOSITION_YES: 07947 ast_data_add_str(enum_node, "text", "yes"); 07948 break; 07949 case ANNOUNCEPOSITION_NO: 07950 ast_data_add_str(enum_node, "text", "no"); 07951 break; 07952 default: 07953 ast_data_add_str(enum_node, "text", "unknown"); 07954 break; 07955 } 07956 ast_data_add_int(enum_node, "value", queue->announceposition); 07957 07958 /* add queue members */ 07959 im = ao2_iterator_init(queue->members, 0); 07960 while ((member = ao2_iterator_next(&im))) { 07961 if (!data_members) { 07962 data_members = ast_data_add_node(data_queue, "members"); 07963 if (!data_members) { 07964 ao2_ref(member, -1); 07965 continue; 07966 } 07967 } 07968 07969 data_member = ast_data_add_node(data_members, "member"); 07970 if (!data_member) { 07971 ao2_ref(member, -1); 07972 continue; 07973 } 07974 07975 ast_data_add_structure(member, data_member, member); 07976 07977 ao2_ref(member, -1); 07978 } 07979 07980 /* include the callers inside the result. */ 07981 if (queue->head) { 07982 for (qe = queue->head; qe; qe = qe->next) { 07983 if (!data_callers) { 07984 data_callers = ast_data_add_node(data_queue, "callers"); 07985 if (!data_callers) { 07986 continue; 07987 } 07988 } 07989 07990 data_caller = ast_data_add_node(data_callers, "caller"); 07991 if (!data_caller) { 07992 continue; 07993 } 07994 07995 ast_data_add_structure(queue_ent, data_caller, qe); 07996 07997 /* add the caller channel. */ 07998 data_caller_channel = ast_data_add_node(data_caller, "channel"); 07999 if (!data_caller_channel) { 08000 continue; 08001 } 08002 08003 ast_channel_data_add_structure(data_caller_channel, qe->chan, 1); 08004 } 08005 } 08006 08007 /* if this queue doesn't match remove the added queue. */ 08008 if (!ast_data_search_match(search, data_queue)) { 08009 ast_data_remove_node(data_root, data_queue); 08010 } 08011 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 2576 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::holdtime, and queue_ent::parent.
02577 { 02578 int oldvalue; 02579 02580 /* Calculate holdtime using an exponential average */ 02581 /* Thanks to SRT for this contribution */ 02582 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 02583 02584 ao2_lock(qe->parent); 02585 oldvalue = qe->parent->holdtime; 02586 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 02587 ao2_unlock(qe->parent); 02588 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Record that a caller gave up on waiting in queue.
Definition at line 3180 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, manager_event, call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, set_queue_variables(), queue_ent::start, and ast_channel::uniqueid.
Referenced by queue_exec().
03181 { 03182 set_queue_variables(qe->parent, qe->chan); 03183 ao2_lock(qe->parent); 03184 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 03185 "Queue: %s\r\n" 03186 "Uniqueid: %s\r\n" 03187 "Position: %d\r\n" 03188 "OriginalPosition: %d\r\n" 03189 "HoldTime: %d\r\n", 03190 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 03191 03192 qe->parent->callsabandoned++; 03193 ao2_unlock(qe->parent); 03194 }
static int reload | ( | void | ) | [static] |
Definition at line 8191 of file app_queue.c.
References AST_FLAGS_ALL, ast_unload_realtime(), QUEUE_RESET_STATS, and reload_handler().
08192 { 08193 struct ast_flags mask = {AST_FLAGS_ALL & ~QUEUE_RESET_STATS,}; 08194 ast_unload_realtime("queue_members"); 08195 reload_handler(1, &mask, NULL); 08196 return 0; 08197 }
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 6686 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().
06687 { 06688 int res = 0; 06689 06690 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) { 06691 res |= reload_queue_rules(reload); 06692 } 06693 if (ast_test_flag(mask, QUEUE_RESET_STATS)) { 06694 res |= clear_stats(queuename); 06695 } 06696 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) { 06697 res |= reload_queues(reload, mask, queuename); 06698 } 06699 return res; 06700 }
static void reload_queue_members | ( | void | ) | [static] |
Reload dynamic queue members persisted into the astdb.
Definition at line 5250 of file app_queue.c.
References add_to_queue(), ao2_lock, ao2_t_find, ao2_unlock, ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), ast_debug, ast_log(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_t_unref, RES_OUTOFMEMORY, member::state_interface, and strsep().
Referenced by load_module().
05251 { 05252 char *cur_ptr; 05253 const char *queue_name; 05254 char *member; 05255 char *interface; 05256 char *membername = NULL; 05257 char *state_interface; 05258 char *penalty_tok; 05259 int penalty = 0; 05260 char *paused_tok; 05261 int paused = 0; 05262 struct ast_db_entry *db_tree; 05263 struct ast_db_entry *entry; 05264 struct call_queue *cur_queue; 05265 char queue_data[PM_MAX_LEN]; 05266 05267 ao2_lock(queues); 05268 05269 /* Each key in 'pm_family' is the name of a queue */ 05270 db_tree = ast_db_gettree(pm_family, NULL); 05271 for (entry = db_tree; entry; entry = entry->next) { 05272 05273 queue_name = entry->key + strlen(pm_family) + 2; 05274 05275 { 05276 struct call_queue tmpq = { 05277 .name = queue_name, 05278 }; 05279 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members"); 05280 } 05281 05282 if (!cur_queue) 05283 cur_queue = load_realtime_queue(queue_name); 05284 05285 if (!cur_queue) { 05286 /* If the queue no longer exists, remove it from the 05287 * database */ 05288 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 05289 ast_db_del(pm_family, queue_name); 05290 continue; 05291 } 05292 05293 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) { 05294 queue_t_unref(cur_queue, "Expire reload reference"); 05295 continue; 05296 } 05297 05298 cur_ptr = queue_data; 05299 while ((member = strsep(&cur_ptr, ",|"))) { 05300 if (ast_strlen_zero(member)) 05301 continue; 05302 05303 interface = strsep(&member, ";"); 05304 penalty_tok = strsep(&member, ";"); 05305 paused_tok = strsep(&member, ";"); 05306 membername = strsep(&member, ";"); 05307 state_interface = strsep(&member, ";"); 05308 05309 if (!penalty_tok) { 05310 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 05311 break; 05312 } 05313 penalty = strtol(penalty_tok, NULL, 10); 05314 if (errno == ERANGE) { 05315 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 05316 break; 05317 } 05318 05319 if (!paused_tok) { 05320 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 05321 break; 05322 } 05323 paused = strtol(paused_tok, NULL, 10); 05324 if ((errno == ERANGE) || paused < 0 || paused > 1) { 05325 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 05326 break; 05327 } 05328 05329 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 05330 05331 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) { 05332 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 05333 break; 05334 } 05335 } 05336 queue_t_unref(cur_queue, "Expire reload reference"); 05337 } 05338 05339 ao2_unlock(queues); 05340 if (db_tree) { 05341 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 05342 ast_db_freetree(db_tree); 05343 } 05344 }
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 6284 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_flags, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, ast_variable::next, rule_list::rules, and ast_variable::value.
Referenced by reload_handler().
06285 { 06286 struct ast_config *cfg; 06287 struct rule_list *rl_iter, *new_rl; 06288 struct penalty_rule *pr_iter; 06289 char *rulecat = NULL; 06290 struct ast_variable *rulevar = NULL; 06291 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 06292 06293 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) { 06294 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n"); 06295 return AST_MODULE_LOAD_SUCCESS; 06296 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 06297 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n"); 06298 return AST_MODULE_LOAD_SUCCESS; 06299 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 06300 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n"); 06301 return AST_MODULE_LOAD_SUCCESS; 06302 } 06303 06304 AST_LIST_LOCK(&rule_lists); 06305 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) { 06306 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list))) 06307 ast_free(pr_iter); 06308 ast_free(rl_iter); 06309 } 06310 while ((rulecat = ast_category_browse(cfg, rulecat))) { 06311 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) { 06312 AST_LIST_UNLOCK(&rule_lists); 06313 return AST_MODULE_LOAD_FAILURE; 06314 } else { 06315 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name)); 06316 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list); 06317 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next) 06318 if(!strcasecmp(rulevar->name, "penaltychange")) 06319 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno); 06320 else 06321 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno); 06322 } 06323 } 06324 AST_LIST_UNLOCK(&rule_lists); 06325 06326 ast_config_destroy(cfg); 06327 06328 return AST_MODULE_LOAD_SUCCESS; 06329 }
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 6598 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_flags, 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_PARAMETERS, queue_set_global_params(), and reload_single_queue().
Referenced by reload_handler().
06599 { 06600 struct ast_config *cfg; 06601 char *cat; 06602 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 06603 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 06604 06605 if (!(cfg = ast_config_load("queues.conf", config_flags))) { 06606 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 06607 return -1; 06608 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 06609 return 0; 06610 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 06611 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n"); 06612 return -1; 06613 } 06614 06615 /* We've made it here, so it looks like we're doing operations on all queues. */ 06616 ao2_lock(queues); 06617 06618 /* Mark all queues as dead for the moment if we're reloading queues. 06619 * For clarity, we could just be reloading members, in which case we don't want to mess 06620 * with the other queue parameters at all*/ 06621 if (queue_reload) { 06622 ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename); 06623 } 06624 06625 /* Chug through config file */ 06626 cat = NULL; 06627 while ((cat = ast_category_browse(cfg, cat)) ) { 06628 if (!strcasecmp(cat, "general") && queue_reload) { 06629 queue_set_global_params(cfg); 06630 continue; 06631 } 06632 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename)) 06633 reload_single_queue(cfg, mask, cat); 06634 } 06635 06636 ast_config_destroy(cfg); 06637 /* Unref all the dead queues if we were reloading queues */ 06638 if (queue_reload) { 06639 ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename); 06640 } 06641 ao2_unlock(queues); 06642 return 0; 06643 }
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 6362 of file app_queue.c.
References ao2_find, ao2_link, ao2_ref, 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, call_queue::membercount, call_queue::members, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, and member::penalty.
Referenced by reload_single_queue().
06363 { 06364 char *membername, *interface, *state_interface, *tmp; 06365 char *parse; 06366 struct member *cur, *newm; 06367 struct member tmpmem; 06368 int penalty; 06369 AST_DECLARE_APP_ARGS(args, 06370 AST_APP_ARG(interface); 06371 AST_APP_ARG(penalty); 06372 AST_APP_ARG(membername); 06373 AST_APP_ARG(state_interface); 06374 ); 06375 06376 if (ast_strlen_zero(memberdata)) { 06377 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n"); 06378 return; 06379 } 06380 06381 /* Add a new member */ 06382 parse = ast_strdupa(memberdata); 06383 06384 AST_STANDARD_APP_ARGS(args, parse); 06385 06386 interface = args.interface; 06387 if (!ast_strlen_zero(args.penalty)) { 06388 tmp = args.penalty; 06389 ast_strip(tmp); 06390 penalty = atoi(tmp); 06391 if (penalty < 0) { 06392 penalty = 0; 06393 } 06394 } else { 06395 penalty = 0; 06396 } 06397 06398 if (!ast_strlen_zero(args.membername)) { 06399 membername = args.membername; 06400 ast_strip(membername); 06401 } else { 06402 membername = interface; 06403 } 06404 06405 if (!ast_strlen_zero(args.state_interface)) { 06406 state_interface = args.state_interface; 06407 ast_strip(state_interface); 06408 } else { 06409 state_interface = interface; 06410 } 06411 06412 /* Find the old position in the list */ 06413 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 06414 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 06415 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) { 06416 ao2_link(q->members, newm); 06417 ao2_ref(newm, -1); 06418 } 06419 newm = NULL; 06420 06421 if (cur) { 06422 ao2_ref(cur, -1); 06423 } else { 06424 q->membercount++; 06425 } 06426 }
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 6468 of file app_queue.c.
References alloc_queue(), ao2_callback, ao2_lock, ao2_t_find, ao2_unlock, ast_log(), ast_test_flag, ast_variable_browse(), ast_variable_retrieve(), call_queue::found, init_queue(), LOG_WARNING, mark_member_dead(), call_queue::membercount, call_queue::members, call_queue::name, OBJ_NODATA, OBJ_POINTER, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, reload_single_member(), strat2int(), call_queue::strategy, and var.
Referenced by reload_queues().
06469 { 06470 int new; 06471 struct call_queue *q = NULL; 06472 /*We're defining a queue*/ 06473 struct call_queue tmpq = { 06474 .name = queuename, 06475 }; 06476 const char *tmpvar; 06477 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 06478 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER); 06479 int prev_weight = 0; 06480 struct ast_variable *var; 06481 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) { 06482 if (queue_reload) { 06483 /* Make one then */ 06484 if (!(q = alloc_queue(queuename))) { 06485 return; 06486 } 06487 } else { 06488 /* Since we're not reloading queues, this means that we found a queue 06489 * in the configuration file which we don't know about yet. Just return. 06490 */ 06491 return; 06492 } 06493 new = 1; 06494 } else { 06495 new = 0; 06496 } 06497 06498 if (!new) { 06499 ao2_lock(q); 06500 prev_weight = q->weight ? 1 : 0; 06501 } 06502 /* Check if we already found a queue with this name in the config file */ 06503 if (q->found) { 06504 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename); 06505 if (!new) { 06506 /* It should be impossible to *not* hit this case*/ 06507 ao2_unlock(q); 06508 } 06509 queue_t_unref(q, "We exist! Expiring temporary pointer"); 06510 return; 06511 } 06512 /* Due to the fact that the "linear" strategy will have a different allocation 06513 * scheme for queue members, we must devise the queue's strategy before other initializations. 06514 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2 06515 * container used will have only a single bucket instead of the typical number. 06516 */ 06517 if (queue_reload) { 06518 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) { 06519 q->strategy = strat2int(tmpvar); 06520 if (q->strategy < 0) { 06521 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 06522 tmpvar, q->name); 06523 q->strategy = QUEUE_STRATEGY_RINGALL; 06524 } 06525 } else { 06526 q->strategy = QUEUE_STRATEGY_RINGALL; 06527 } 06528 init_queue(q); 06529 } 06530 if (member_reload) { 06531 q->membercount = 0; 06532 ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL); 06533 } 06534 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) { 06535 if (member_reload && !strcasecmp(var->name, "member")) { 06536 reload_single_member(var->value, q); 06537 } else if (queue_reload) { 06538 queue_set_param(q, var->name, var->value, var->lineno, 1); 06539 } 06540 } 06541 /* At this point, we've determined if the queue has a weight, so update use_weight 06542 * as appropriate 06543 */ 06544 if (!q->weight && prev_weight) { 06545 ast_atomic_fetchadd_int(&use_weight, -1); 06546 } 06547 else if (q->weight && !prev_weight) { 06548 ast_atomic_fetchadd_int(&use_weight, +1); 06549 } 06550 06551 /* Free remaining members marked as delme */ 06552 if (member_reload) { 06553 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q); 06554 } 06555 06556 if (new) { 06557 queues_t_link(queues, q, "Add queue to container"); 06558 } else { 06559 ao2_unlock(q); 06560 } 06561 queue_t_unref(q, "Expiring creation reference"); 06562 }
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 4985 of file app_queue.c.
References ao2_find, ao2_lock, ao2_ref, ao2_t_find, ao2_unlink, ao2_unlock, ast_copy_string(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event, call_queue::membercount, member::membername, call_queue::members, call_queue::name, OBJ_POINTER, queue_t_unref, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.
Referenced by attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().
04986 { 04987 struct call_queue *q, tmpq = { 04988 .name = queuename, 04989 }; 04990 struct member *mem, tmpmem; 04991 int res = RES_NOSUCHQUEUE; 04992 04993 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04994 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) { 04995 ao2_lock(queues); 04996 ao2_lock(q); 04997 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 04998 /* XXX future changes should beware of this assumption!! */ 04999 if (!mem->dynamic) { 05000 ao2_ref(mem, -1); 05001 ao2_unlock(q); 05002 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference"); 05003 ao2_unlock(queues); 05004 return RES_NOT_DYNAMIC; 05005 } 05006 q->membercount--; 05007 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 05008 "Queue: %s\r\n" 05009 "Location: %s\r\n" 05010 "MemberName: %s\r\n", 05011 q->name, mem->interface, mem->membername); 05012 ao2_unlink(q->members, mem); 05013 ao2_ref(mem, -1); 05014 05015 if (queue_persistent_members) 05016 dump_queue_members(q); 05017 05018 res = RES_OKAY; 05019 } else { 05020 res = RES_EXISTS; 05021 } 05022 ao2_unlock(q); 05023 ao2_unlock(queues); 05024 queue_t_unref(q, "Expiring temporary reference"); 05025 } 05026 05027 return res; 05028 }
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 2839 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_call(), ast_cdr_busy(), ast_cdr_isset_unanswered(), ast_cdr_setdestchan(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_set_caller_event(), ast_channel_trylock, ast_channel_unlock, ast_connected_line_copy_from_caller(), ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, AST_FLAG_ANSWERED_ELSEWHERE, ast_party_caller_set_init(), ast_party_redirecting_copy(), ast_request(), ast_set_callerid(), ast_set_flag, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::caller, queue_ent::cancel_answered_elsewhere, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, CHANNEL_DEADLOCK_AVOIDANCE, ast_cdr::clid, compare_weight(), 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, get_queue_member_status(), ast_party_connected_line::id, ast_party_caller::id, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, queue_ent::linpos, ast_channel::macroexten, manager_event, callattempt::member, member::membername, ast_party_id::name, ast_channel::name, call_queue::name, ast_channel::nativeformats, ast_party_dialed::number, ast_party_id::number, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, ast_channel::redirecting, call_queue::ringinuse, call_queue::rrpos, S_COR, S_OR, ast_cdr::src, member::status, status, callattempt::stillgoing, ast_party_name::str, ast_party_number::str, ast_party_dialed::str, ast_party_dialed::transit_network_select, ast_channel::uniqueid, update_status(), ast_cdr::userfield, ast_party_name::valid, ast_party_number::valid, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.
Referenced by ring_one().
02840 { 02841 int res; 02842 int status; 02843 char tech[256]; 02844 char *location; 02845 const char *macrocontext, *macroexten; 02846 02847 /* on entry here, we know that tmp->chan == NULL */ 02848 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) || 02849 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) { 02850 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 02851 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface); 02852 if (qe->chan->cdr) 02853 ast_cdr_busy(qe->chan->cdr); 02854 tmp->stillgoing = 0; 02855 (*busies)++; 02856 return 0; 02857 } 02858 02859 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 02860 ast_debug(1, "%s in use, can't receive call\n", tmp->interface); 02861 if (qe->chan->cdr) 02862 ast_cdr_busy(qe->chan->cdr); 02863 tmp->stillgoing = 0; 02864 return 0; 02865 } 02866 02867 if (tmp->member->paused) { 02868 ast_debug(1, "%s paused, can't receive call\n", tmp->interface); 02869 if (qe->chan->cdr) 02870 ast_cdr_busy(qe->chan->cdr); 02871 tmp->stillgoing = 0; 02872 return 0; 02873 } 02874 if (use_weight && compare_weight(qe->parent,tmp->member)) { 02875 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 02876 if (qe->chan->cdr) 02877 ast_cdr_busy(qe->chan->cdr); 02878 tmp->stillgoing = 0; 02879 (*busies)++; 02880 return 0; 02881 } 02882 02883 ast_copy_string(tech, tmp->interface, sizeof(tech)); 02884 if ((location = strchr(tech, '/'))) 02885 *location++ = '\0'; 02886 else 02887 location = ""; 02888 02889 /* Request the peer */ 02890 tmp->chan = ast_request(tech, qe->chan->nativeformats, qe->chan, location, &status); 02891 if (!tmp->chan) { /* If we can't, just go on to the next call */ 02892 if (qe->chan->cdr) 02893 ast_cdr_busy(qe->chan->cdr); 02894 tmp->stillgoing = 0; 02895 02896 ao2_lock(qe->parent); 02897 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); 02898 qe->parent->rrpos++; 02899 qe->linpos++; 02900 ao2_unlock(qe->parent); 02901 02902 (*busies)++; 02903 return 0; 02904 } 02905 02906 ast_channel_lock(tmp->chan); 02907 while (ast_channel_trylock(qe->chan)) { 02908 CHANNEL_DEADLOCK_AVOIDANCE(tmp->chan); 02909 } 02910 02911 if (qe->cancel_answered_elsewhere) { 02912 ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); 02913 } 02914 tmp->chan->appl = "AppQueue"; 02915 tmp->chan->data = "(Outgoing Line)"; 02916 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); 02917 02918 /* If the new channel has no callerid, try to guess what it should be */ 02919 if (!tmp->chan->caller.id.number.valid) { 02920 if (qe->chan->connected.id.number.valid) { 02921 struct ast_party_caller caller; 02922 02923 ast_party_caller_set_init(&caller, &tmp->chan->caller); 02924 caller.id = qe->chan->connected.id; 02925 caller.ani = qe->chan->connected.ani; 02926 ast_channel_set_caller_event(tmp->chan, &caller, NULL); 02927 } else if (!ast_strlen_zero(qe->chan->dialed.number.str)) { 02928 ast_set_callerid(tmp->chan, qe->chan->dialed.number.str, NULL, NULL); 02929 } else if (!ast_strlen_zero(S_OR(qe->chan->macroexten, qe->chan->exten))) { 02930 ast_set_callerid(tmp->chan, S_OR(qe->chan->macroexten, qe->chan->exten), NULL, NULL); 02931 } 02932 tmp->dial_callerid_absent = 1; 02933 } 02934 02935 ast_party_redirecting_copy(&tmp->chan->redirecting, &qe->chan->redirecting); 02936 02937 tmp->chan->dialed.transit_network_select = qe->chan->dialed.transit_network_select; 02938 02939 ast_connected_line_copy_from_caller(&tmp->chan->connected, &qe->chan->caller); 02940 02941 /* Inherit specially named variables from parent channel */ 02942 ast_channel_inherit_variables(qe->chan, tmp->chan); 02943 ast_channel_datastore_inherit(qe->chan, tmp->chan); 02944 02945 /* Presense of ADSI CPE on outgoing channel follows ours */ 02946 tmp->chan->adsicpe = qe->chan->adsicpe; 02947 02948 /* Inherit context and extension */ 02949 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 02950 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); 02951 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 02952 if (!ast_strlen_zero(macroexten)) 02953 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 02954 else 02955 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 02956 if (ast_cdr_isset_unanswered()) { 02957 /* they want to see the unanswered dial attempts! */ 02958 /* set up the CDR fields on all the CDRs to give sensical information */ 02959 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name); 02960 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid); 02961 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel); 02962 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src); 02963 strcpy(tmp->chan->cdr->dst, qe->chan->exten); 02964 strcpy(tmp->chan->cdr->dcontext, qe->chan->context); 02965 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp); 02966 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata); 02967 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags; 02968 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode); 02969 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield); 02970 } 02971 02972 /* Place the call, but don't wait on the answer */ 02973 if ((res = ast_call(tmp->chan, location, 0))) { 02974 /* Again, keep going even if there's an error */ 02975 ast_debug(1, "ast call on peer returned %d\n", res); 02976 ast_verb(3, "Couldn't call %s\n", tmp->interface); 02977 ast_channel_unlock(tmp->chan); 02978 ast_channel_unlock(qe->chan); 02979 do_hang(tmp); 02980 (*busies)++; 02981 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); 02982 return 0; 02983 } else if (qe->parent->eventwhencalled) { 02984 char vars[2048]; 02985 02986 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 02987 "Queue: %s\r\n" 02988 "AgentCalled: %s\r\n" 02989 "AgentName: %s\r\n" 02990 "ChannelCalling: %s\r\n" 02991 "DestinationChannel: %s\r\n" 02992 "CallerIDNum: %s\r\n" 02993 "CallerIDName: %s\r\n" 02994 "Context: %s\r\n" 02995 "Extension: %s\r\n" 02996 "Priority: %d\r\n" 02997 "Uniqueid: %s\r\n" 02998 "%s", 02999 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name, 03000 S_COR(tmp->chan->caller.id.number.valid, tmp->chan->caller.id.number.str, "unknown"), 03001 S_COR(tmp->chan->caller.id.name.valid, tmp->chan->caller.id.name.str, "unknown"), 03002 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid, 03003 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03004 ast_verb(3, "Called %s\n", tmp->interface); 03005 } 03006 ast_channel_unlock(tmp->chan); 03007 ast_channel_unlock(qe->chan); 03008 03009 update_status(qe->parent, tmp->member, get_queue_member_status(tmp->member)); 03010 return 1; 03011 }
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 3039 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 wait_for_answer().
03040 { 03041 int ret = 0; 03042 03043 while (ret == 0) { 03044 struct callattempt *best = find_best(outgoing); 03045 if (!best) { 03046 ast_debug(1, "Nobody left to try ringing in queue\n"); 03047 break; 03048 } 03049 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 03050 struct callattempt *cur; 03051 /* Ring everyone who shares this best metric (for ringall) */ 03052 for (cur = outgoing; cur; cur = cur->q_next) { 03053 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 03054 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 03055 ret |= ring_entry(qe, cur, busies); 03056 } 03057 } 03058 } else { 03059 /* Ring just the best channel */ 03060 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric); 03061 ret = ring_entry(qe, best, busies); 03062 } 03063 03064 /* If we have timed out, break out */ 03065 if (qe->expire && (time(NULL) >= qe->expire)) { 03066 ast_debug(1, "Queue timed out while ringing members.\n"); 03067 ret = 0; 03068 break; 03069 } 03070 } 03071 03072 return ret; 03073 }
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 3197 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, ast_channel::name, call_queue::name, queue_ent::parent, QUEUE_AUTOPAUSE_OFF, QUEUE_AUTOPAUSE_ON, QUEUE_EVENT_VARIABLES, queue_ent::ring_when_ringing, set_member_paused(), ast_channel::uniqueid, and vars2manager().
03198 { 03199 ast_verb(3, "Nobody picked up in %d ms\n", rnatime); 03200 03201 /* Stop ringing, and resume MOH if specified */ 03202 if (qe->ring_when_ringing) { 03203 ast_indicate(qe->chan, -1); 03204 ast_moh_start(qe->chan, qe->moh, NULL); 03205 } 03206 03207 if (qe->parent->eventwhencalled) { 03208 char vars[2048]; 03209 03210 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer", 03211 "Queue: %s\r\n" 03212 "Uniqueid: %s\r\n" 03213 "Channel: %s\r\n" 03214 "Member: %s\r\n" 03215 "MemberName: %s\r\n" 03216 "Ringtime: %d\r\n" 03217 "%s", 03218 qe->parent->name, 03219 qe->chan->uniqueid, 03220 qe->chan->name, 03221 interface, 03222 membername, 03223 rnatime, 03224 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03225 } 03226 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 03227 if (qe->parent->autopause != QUEUE_AUTOPAUSE_OFF && pause) { 03228 if (qe->parent->autopause == QUEUE_AUTOPAUSE_ON) { 03229 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { 03230 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", 03231 interface, qe->parent->name); 03232 } else { 03233 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 03234 } 03235 } else { 03236 /* If queue autopause is mode all, just don't send any queue to stop. 03237 * the function will stop in all queues */ 03238 if (!set_member_paused("", interface, "Auto-Pause", 1)) { 03239 ast_verb(3, "Auto-Pausing Queue Member %s in all queues since they failed to answer on queue %s.\n", 03240 interface, qe->parent->name); 03241 } else { 03242 ast_verb(3, "Failed to pause Queue Member %s in all queues!\n", interface); 03243 } 03244 } 03245 } 03246 return; 03247 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
RemoveQueueMember application.
Definition at line 5419 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, ast_channel::name, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and ast_channel::uniqueid.
Referenced by load_module().
05420 { 05421 int res=-1; 05422 char *parse, *temppos = NULL; 05423 AST_DECLARE_APP_ARGS(args, 05424 AST_APP_ARG(queuename); 05425 AST_APP_ARG(interface); 05426 AST_APP_ARG(options); 05427 ); 05428 05429 05430 if (ast_strlen_zero(data)) { 05431 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n"); 05432 return -1; 05433 } 05434 05435 parse = ast_strdupa(data); 05436 05437 AST_STANDARD_APP_ARGS(args, parse); 05438 05439 if (ast_strlen_zero(args.interface)) { 05440 args.interface = ast_strdupa(chan->name); 05441 temppos = strrchr(args.interface, '-'); 05442 if (temppos) 05443 *temppos = '\0'; 05444 } 05445 05446 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface); 05447 05448 switch (remove_from_queue(args.queuename, args.interface)) { 05449 case RES_OKAY: 05450 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 05451 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 05452 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 05453 res = 0; 05454 break; 05455 case RES_EXISTS: 05456 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 05457 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 05458 res = 0; 05459 break; 05460 case RES_NOSUCHQUEUE: 05461 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 05462 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 05463 res = 0; 05464 break; 05465 case RES_NOT_DYNAMIC: 05466 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 05467 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 05468 res = 0; 05469 break; 05470 } 05471 05472 return res; 05473 }
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 1916 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_log(), ast_queue_log(), ast_strlen_zero(), create_queue_member(), member::dead, member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::rt_uniqueid, S_OR, and member::state_interface.
Referenced by update_realtime_members().
01917 { 01918 struct member *m; 01919 struct ao2_iterator mem_iter; 01920 int penalty = 0; 01921 int paused = 0; 01922 int found = 0; 01923 01924 if (ast_strlen_zero(rt_uniqueid)) { 01925 ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL")); 01926 return; 01927 } 01928 01929 if (penalty_str) { 01930 penalty = atoi(penalty_str); 01931 if (penalty < 0) 01932 penalty = 0; 01933 } 01934 01935 if (paused_str) { 01936 paused = atoi(paused_str); 01937 if (paused < 0) 01938 paused = 0; 01939 } 01940 01941 /* Find member by realtime uniqueid and update */ 01942 mem_iter = ao2_iterator_init(q->members, 0); 01943 while ((m = ao2_iterator_next(&mem_iter))) { 01944 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) { 01945 m->dead = 0; /* Do not delete this one. */ 01946 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 01947 if (paused_str) 01948 m->paused = paused; 01949 if (strcasecmp(state_interface, m->state_interface)) { 01950 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); 01951 } 01952 m->penalty = penalty; 01953 found = 1; 01954 ao2_ref(m, -1); 01955 break; 01956 } 01957 ao2_ref(m, -1); 01958 } 01959 ao2_iterator_destroy(&mem_iter); 01960 01961 /* Create a new member */ 01962 if (!found) { 01963 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { 01964 m->dead = 0; 01965 m->realtime = 1; 01966 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 01967 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", ""); 01968 ao2_link(q->members, m); 01969 ao2_ref(m, -1); 01970 m = NULL; 01971 q->membercount++; 01972 } 01973 } 01974 }
static int say_periodic_announcement | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Playback announcement to queued members if peroid has elapsed.
Definition at line 3124 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().
03125 { 03126 int res = 0; 03127 time_t now; 03128 03129 /* Get the current time */ 03130 time(&now); 03131 03132 /* Check to see if it is time to announce */ 03133 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 03134 return 0; 03135 03136 /* Stop the music on hold so we can play our own file */ 03137 if (ringing) 03138 ast_indicate(qe->chan,-1); 03139 else 03140 ast_moh_stop(qe->chan); 03141 03142 ast_verb(3, "Playing periodic announcement\n"); 03143 03144 if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) { 03145 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce; 03146 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 03147 ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) { 03148 qe->last_periodic_announce_sound = 0; 03149 } 03150 03151 /* play the announcement */ 03152 res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])); 03153 03154 if (res > 0 && !valid_exit(qe, res)) 03155 res = 0; 03156 03157 /* Resume Music on Hold if the caller is going to stay in the queue */ 03158 if (!res) { 03159 if (ringing) 03160 ast_indicate(qe->chan, AST_CONTROL_RINGING); 03161 else 03162 ast_moh_start(qe->chan, qe->moh, NULL); 03163 } 03164 03165 /* update last_periodic_announce_time */ 03166 if (qe->parent->relativeperiodicannounce) 03167 time(&qe->last_periodic_announce_time); 03168 else 03169 qe->last_periodic_announce_time = now; 03170 03171 /* Update the current periodic announcement to the next announcement */ 03172 if (!qe->parent->randomperiodicannounce) { 03173 qe->last_periodic_announce_sound++; 03174 } 03175 03176 return res; 03177 }
static int say_position | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Definition at line 2432 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, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, ast_channel::name, call_queue::name, queue_ent::parent, play_file(), queue_ent::pos, call_queue::queue_quantity1, call_queue::queue_quantity2, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, and valid_exit().
Referenced by queue_exec().
02433 { 02434 int res = 0, avgholdmins, avgholdsecs, announceposition = 0; 02435 int say_thanks = 1; 02436 time_t now; 02437 02438 /* Let minannouncefrequency seconds pass between the start of each position announcement */ 02439 time(&now); 02440 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) 02441 return 0; 02442 02443 /* If either our position has changed, or we are over the freq timer, say position */ 02444 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 02445 return 0; 02446 02447 if (ringing) { 02448 ast_indicate(qe->chan,-1); 02449 } else { 02450 ast_moh_stop(qe->chan); 02451 } 02452 02453 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES || 02454 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN || 02455 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT && 02456 qe->pos <= qe->parent->announcepositionlimit)) 02457 announceposition = 1; 02458 02459 02460 if (announceposition == 1) { 02461 /* Say we're next, if we are */ 02462 if (qe->pos == 1) { 02463 res = play_file(qe->chan, qe->parent->sound_next); 02464 if (res) 02465 goto playout; 02466 else 02467 goto posout; 02468 } else { 02469 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02470 /* More than Case*/ 02471 res = play_file(qe->chan, qe->parent->queue_quantity1); 02472 if (res) 02473 goto playout; 02474 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02475 if (res) 02476 goto playout; 02477 } else { 02478 /* Normal Case */ 02479 res = play_file(qe->chan, qe->parent->sound_thereare); 02480 if (res) 02481 goto playout; 02482 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02483 if (res) 02484 goto playout; 02485 } 02486 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02487 /* More than Case*/ 02488 res = play_file(qe->chan, qe->parent->queue_quantity2); 02489 if (res) 02490 goto playout; 02491 } else { 02492 res = play_file(qe->chan, qe->parent->sound_calls); 02493 if (res) 02494 goto playout; 02495 } 02496 } 02497 } 02498 /* Round hold time to nearest minute */ 02499 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 02500 02501 /* If they have specified a rounding then round the seconds as well */ 02502 if (qe->parent->roundingseconds) { 02503 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 02504 avgholdsecs *= qe->parent->roundingseconds; 02505 } else { 02506 avgholdsecs = 0; 02507 } 02508 02509 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 02510 02511 /* If the hold time is >1 min, if it's enabled, and if it's not 02512 supposed to be only once and we have already said it, say it */ 02513 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 02514 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 02515 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 02516 res = play_file(qe->chan, qe->parent->sound_holdtime); 02517 if (res) 02518 goto playout; 02519 02520 if (avgholdmins >= 1) { 02521 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 02522 if (res) 02523 goto playout; 02524 02525 if (avgholdmins == 1) { 02526 res = play_file(qe->chan, qe->parent->sound_minute); 02527 if (res) 02528 goto playout; 02529 } else { 02530 res = play_file(qe->chan, qe->parent->sound_minutes); 02531 if (res) 02532 goto playout; 02533 } 02534 } 02535 if (avgholdsecs >= 1) { 02536 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 02537 if (res) 02538 goto playout; 02539 02540 res = play_file(qe->chan, qe->parent->sound_seconds); 02541 if (res) 02542 goto playout; 02543 } 02544 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) { 02545 say_thanks = 0; 02546 } 02547 02548 posout: 02549 if (qe->parent->announceposition) { 02550 ast_verb(3, "Told %s in %s their queue position (which was %d)\n", 02551 qe->chan->name, qe->parent->name, qe->pos); 02552 } 02553 if (say_thanks) { 02554 res = play_file(qe->chan, qe->parent->sound_thanks); 02555 } 02556 playout: 02557 02558 if ((res > 0 && !valid_exit(qe, res))) 02559 res = 0; 02560 02561 /* Set our last_pos indicators */ 02562 qe->last_pos = now; 02563 qe->last_pos_said = qe->pos; 02564 02565 /* Don't restart music on hold if we're about to exit the caller from the queue */ 02566 if (!res) { 02567 if (ringing) { 02568 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02569 } else { 02570 ast_moh_start(qe->chan, qe->moh, NULL); 02571 } 02572 } 02573 return res; 02574 }
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 3954 of file app_queue.c.
References AGENT, CALLER, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, member::interface, manager_event, member::membername, ast_channel::name, queue_ent::parent, QUEUE_EVENT_VARIABLES, queue_ent::start, ast_channel::uniqueid, and vars2manager().
03957 { 03958 const char *reason = NULL; /* silence dumb compilers */ 03959 03960 if (!qe->parent->eventwhencalled) 03961 return; 03962 03963 switch (rsn) { 03964 case CALLER: 03965 reason = "caller"; 03966 break; 03967 case AGENT: 03968 reason = "agent"; 03969 break; 03970 case TRANSFER: 03971 reason = "transfer"; 03972 break; 03973 } 03974 03975 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03976 "Queue: %s\r\n" 03977 "Uniqueid: %s\r\n" 03978 "Channel: %s\r\n" 03979 "Member: %s\r\n" 03980 "MemberName: %s\r\n" 03981 "HoldTime: %ld\r\n" 03982 "TalkTime: %ld\r\n" 03983 "Reason: %s\r\n" 03984 "%s", 03985 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03986 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason, 03987 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : ""); 03988 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
const char * | reason, | |||
int | paused | |||
) | [static] |
Definition at line 5092 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_WARNING, manager_event, member::membername, call_queue::name, member::paused, queue_t_unref, 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().
05093 { 05094 int found = 0; 05095 struct call_queue *q; 05096 struct member *mem; 05097 struct ao2_iterator queue_iter; 05098 int failed; 05099 05100 /* Special event for when all queues are paused - individual events still generated */ 05101 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 05102 if (ast_strlen_zero(queuename)) 05103 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 05104 05105 queue_iter = ao2_iterator_init(queues, 0); 05106 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) { 05107 ao2_lock(q); 05108 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 05109 if ((mem = interface_exists(q, interface))) { 05110 if (mem->paused == paused) { 05111 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 05112 } 05113 05114 failed = 0; 05115 if (mem->realtime) { 05116 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 05117 } 05118 05119 if (failed) { 05120 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface); 05121 ao2_ref(mem, -1); 05122 ao2_unlock(q); 05123 queue_t_unref(q, "Done with iterator"); 05124 continue; 05125 } 05126 found++; 05127 mem->paused = paused; 05128 05129 if (queue_persistent_members) 05130 dump_queue_members(q); 05131 05132 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, "")); 05133 05134 if (!ast_strlen_zero(reason)) { 05135 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 05136 "Queue: %s\r\n" 05137 "Location: %s\r\n" 05138 "MemberName: %s\r\n" 05139 "Paused: %d\r\n" 05140 "Reason: %s\r\n", 05141 q->name, mem->interface, mem->membername, paused, reason); 05142 } else { 05143 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 05144 "Queue: %s\r\n" 05145 "Location: %s\r\n" 05146 "MemberName: %s\r\n" 05147 "Paused: %d\r\n", 05148 q->name, mem->interface, mem->membername, paused); 05149 } 05150 ao2_ref(mem, -1); 05151 } 05152 } 05153 05154 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) { 05155 ao2_unlock(q); 05156 queue_t_unref(q, "Done with iterator"); 05157 break; 05158 } 05159 05160 ao2_unlock(q); 05161 queue_t_unref(q, "Done with iterator"); 05162 } 05163 ao2_iterator_destroy(&queue_iter); 05164 05165 return found ? RESULT_SUCCESS : RESULT_FAILURE; 05166 }
static int set_member_penalty | ( | const char * | queuename, | |
const char * | interface, | |||
int | penalty | |||
) | [static] |
Definition at line 5169 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock, ao2_ref, ao2_t_iterator_next, ao2_unlock, ast_log(), ast_queue_log(), ast_strlen_zero(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_ERROR, manager_event, call_queue::name, member::penalty, queue_t_unref, RESULT_FAILURE, and RESULT_SUCCESS.
Referenced by handle_queue_set_member_penalty(), manager_queue_member_penalty(), and queue_function_memberpenalty_write().
05170 { 05171 int foundinterface = 0, foundqueue = 0; 05172 struct call_queue *q; 05173 struct member *mem; 05174 struct ao2_iterator queue_iter; 05175 05176 if (penalty < 0) { 05177 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); 05178 return RESULT_FAILURE; 05179 } 05180 05181 queue_iter = ao2_iterator_init(queues, 0); 05182 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 05183 ao2_lock(q); 05184 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 05185 foundqueue++; 05186 if ((mem = interface_exists(q, interface))) { 05187 foundinterface++; 05188 mem->penalty = penalty; 05189 05190 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); 05191 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", 05192 "Queue: %s\r\n" 05193 "Location: %s\r\n" 05194 "Penalty: %d\r\n", 05195 q->name, mem->interface, penalty); 05196 ao2_ref(mem, -1); 05197 } 05198 } 05199 ao2_unlock(q); 05200 queue_t_unref(q, "Done with iterator"); 05201 } 05202 ao2_iterator_destroy(&queue_iter); 05203 05204 if (foundinterface) { 05205 return RESULT_SUCCESS; 05206 } else if (!foundqueue) { 05207 ast_log (LOG_ERROR, "Invalid queuename\n"); 05208 } else { 05209 ast_log (LOG_ERROR, "Invalid interface\n"); 05210 } 05211 05212 return RESULT_FAILURE; 05213 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 1037 of file app_queue.c.
References ARRAY_LEN, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
01038 { 01039 int i; 01040 01041 for (i = 0; i < ARRAY_LEN(queue_results); i++) { 01042 if (queue_results[i].id == res) { 01043 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 01044 return; 01045 } 01046 } 01047 }
static void set_queue_variables | ( | struct call_queue * | q, | |
struct ast_channel * | chan | |||
) | [static] |
Set variables of queue.
Definition at line 1132 of file app_queue.c.
References ao2_lock, ao2_unlock, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, call_queue::holdtime, int2strat(), call_queue::maxlen, call_queue::name, pbx_builtin_setvar_multiple(), call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.
Referenced by end_bridge_callback(), and record_abandoned().
01133 { 01134 char interfacevar[256]=""; 01135 float sl = 0; 01136 01137 ao2_lock(q); 01138 01139 if (q->setqueuevar) { 01140 sl = 0; 01141 if (q->callscompleted > 0) 01142 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 01143 01144 snprintf(interfacevar, sizeof(interfacevar), 01145 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 01146 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 01147 01148 ao2_unlock(q); 01149 01150 pbx_builtin_setvar_multiple(chan, interfacevar); 01151 } else { 01152 ao2_unlock(q); 01153 } 01154 }
static struct ast_datastore* setup_transfer_datastore | ( | struct queue_ent * | qe, | |
struct member * | member, | |||
time_t | starttime, | |||
int | callcompletedinsl | |||
) | [static] |
create a datastore for storing relevant info to log attended transfers in the queue_log
Definition at line 4058 of file app_queue.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_datastore_alloc, ast_log(), queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, and queue_transfer_info.
04059 { 04060 struct ast_datastore *ds; 04061 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 04062 04063 if (!qtds) { 04064 ast_log(LOG_WARNING, "Memory allocation error!\n"); 04065 return NULL; 04066 } 04067 04068 ast_channel_lock(qe->chan); 04069 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) { 04070 ast_channel_unlock(qe->chan); 04071 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 04072 return NULL; 04073 } 04074 04075 qtds->qe = qe; 04076 /* This member is refcounted in try_calling, so no need to add it here, too */ 04077 qtds->member = member; 04078 qtds->starttime = starttime; 04079 qtds->callcompletedinsl = callcompletedinsl; 04080 ds->data = qtds; 04081 ast_channel_datastore_add(qe->chan, ds); 04082 ast_channel_unlock(qe->chan); 04083 return ds; 04084 }
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 3100 of file app_queue.c.
References ast_debug, find_best(), callattempt::interface, queue_ent::linpos, queue_ent::linwrapped, and callattempt::metric.
03101 { 03102 struct callattempt *best = find_best(outgoing); 03103 03104 if (best) { 03105 /* Ring just the best channel */ 03106 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 03107 qe->linpos = best->metric % 1000; 03108 } else { 03109 /* Just increment rrpos */ 03110 if (qe->linwrapped) { 03111 /* No more channels, start over */ 03112 qe->linpos = 0; 03113 } else { 03114 /* Prioritize next entry */ 03115 qe->linpos++; 03116 } 03117 } 03118 qe->linwrapped = 0; 03119 03120 return 0; 03121 }
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 3076 of file app_queue.c.
References ast_debug, find_best(), callattempt::interface, callattempt::metric, queue_ent::parent, call_queue::rrpos, and call_queue::wrapped.
03077 { 03078 struct callattempt *best = find_best(outgoing); 03079 03080 if (best) { 03081 /* Ring just the best channel */ 03082 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 03083 qe->parent->rrpos = best->metric % 1000; 03084 } else { 03085 /* Just increment rrpos */ 03086 if (qe->parent->wrapped) { 03087 /* No more channels, start over */ 03088 qe->parent->rrpos = 0; 03089 } else { 03090 /* Prioritize next entry */ 03091 qe->parent->rrpos++; 03092 } 03093 } 03094 qe->parent->wrapped = 0; 03095 03096 return 0; 03097 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 1061 of file app_queue.c.
References ARRAY_LEN, strategies, and strategy::strategy.
Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_single_queue().
01062 { 01063 int x; 01064 01065 for (x = 0; x < ARRAY_LEN(strategies); x++) { 01066 if (!strcasecmp(strategy, strategies[x].name)) 01067 return strategies[x].strategy; 01068 } 01069 01070 return -1; 01071 }
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 4138 of file app_queue.c.
References ast_channel_datastore_find(), ast_channel_lock, ast_channel_unlock, 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_MAX_CONTEXT, AST_MAX_EXTENSION, ast_set_flag, ast_test_flag, queue_ent::cancel_answered_elsewhere, queue_ent::chan, dialed_interface_info, queue_ent::expire, call_queue::membercount, queue_ent::parent, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, and call_queue::strategy.
Referenced by queue_exec().
04139 { 04140 struct member *cur; 04141 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 04142 int to, orig; 04143 char oldexten[AST_MAX_EXTENSION]=""; 04144 char oldcontext[AST_MAX_CONTEXT]=""; 04145 char queuename[256]=""; 04146 char interfacevar[256]=""; 04147 struct ast_channel *peer; 04148 struct ast_channel *which; 04149 struct callattempt *lpeer; 04150 struct member *member; 04151 struct ast_app *application; 04152 int res = 0, bridge = 0; 04153 int numbusies = 0; 04154 int x=0; 04155 char *announce = NULL; 04156 char digit = 0; 04157 time_t callstart; 04158 time_t now = time(NULL); 04159 struct ast_bridge_config bridge_config; 04160 char nondataquality = 1; 04161 char *agiexec = NULL; 04162 char *macroexec = NULL; 04163 char *gosubexec = NULL; 04164 int ret = 0; 04165 const char *monitorfilename; 04166 const char *monitor_exec; 04167 const char *monitor_options; 04168 char tmpid[256], tmpid2[256]; 04169 char meid[1024], meid2[1024]; 04170 char mixmonargs[1512]; 04171 struct ast_app *mixmonapp = NULL; 04172 char *p; 04173 char vars[2048]; 04174 int forwardsallowed = 1; 04175 int update_connectedline = 1; 04176 int callcompletedinsl; 04177 struct ao2_iterator memi; 04178 struct ast_datastore *datastore, *transfer_ds; 04179 struct queue_end_bridge *queue_end_bridge = NULL; 04180 const int need_weight = use_weight; 04181 04182 ast_channel_lock(qe->chan); 04183 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 04184 ast_channel_unlock(qe->chan); 04185 04186 memset(&bridge_config, 0, sizeof(bridge_config)); 04187 tmpid[0] = 0; 04188 meid[0] = 0; 04189 time(&now); 04190 04191 /* If we've already exceeded our timeout, then just stop 04192 * This should be extremely rare. queue_exec will take care 04193 * of removing the caller and reporting the timeout as the reason. 04194 */ 04195 if (qe->expire && now >= qe->expire) { 04196 res = 0; 04197 goto out; 04198 } 04199 04200 for (; options && *options; options++) 04201 switch (*options) { 04202 case 't': 04203 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 04204 break; 04205 case 'T': 04206 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 04207 break; 04208 case 'w': 04209 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 04210 break; 04211 case 'W': 04212 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 04213 break; 04214 case 'c': 04215 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN); 04216 break; 04217 case 'd': 04218 nondataquality = 0; 04219 break; 04220 case 'h': 04221 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 04222 break; 04223 case 'H': 04224 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 04225 break; 04226 case 'k': 04227 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL); 04228 break; 04229 case 'K': 04230 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL); 04231 break; 04232 case 'n': 04233 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR) 04234 (*tries)++; 04235 else 04236 *tries = qe->parent->membercount; 04237 *noption = 1; 04238 break; 04239 case 'i': 04240 forwardsallowed = 0; 04241 break; 04242 case 'I': 04243 update_connectedline = 0; 04244 break; 04245 case 'x': 04246 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); 04247 break; 04248 case 'X': 04249 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON); 04250 break; 04251 case 'C': 04252 qe->cancel_answered_elsewhere = 1; 04253 break; 04254 } 04255 04256 /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 04257 (this is mainly to support chan_local) 04258 */ 04259 if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) { 04260 qe->cancel_answered_elsewhere = 1; 04261 } 04262 04263 /* Hold the lock while we setup the outgoing calls */ 04264 if (need_weight) 04265 ao2_lock(queues); 04266 ao2_lock(qe->parent); 04267 ast_debug(1, "%s is trying to call a queue member.\n", 04268 qe->chan->name); 04269 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 04270 if (!ast_strlen_zero(qe->announce)) 04271 announce = qe->announce; 04272 if (!ast_strlen_zero(announceoverride)) 04273 announce = announceoverride; 04274 04275 memi = ao2_iterator_init(qe->parent->members, 0); 04276 while ((cur = ao2_iterator_next(&memi))) { 04277 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 04278 struct ast_dialed_interface *di; 04279 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 04280 if (!tmp) { 04281 ao2_ref(cur, -1); 04282 ao2_unlock(qe->parent); 04283 ao2_iterator_destroy(&memi); 04284 if (need_weight) 04285 ao2_unlock(queues); 04286 goto out; 04287 } 04288 if (!datastore) { 04289 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { 04290 ao2_ref(cur, -1); 04291 ao2_unlock(qe->parent); 04292 ao2_iterator_destroy(&memi); 04293 if (need_weight) 04294 ao2_unlock(queues); 04295 callattempt_free(tmp); 04296 goto out; 04297 } 04298 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 04299 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 04300 ao2_ref(cur, -1); 04301 ao2_unlock(&qe->parent); 04302 ao2_iterator_destroy(&memi); 04303 if (need_weight) 04304 ao2_unlock(queues); 04305 callattempt_free(tmp); 04306 goto out; 04307 } 04308 datastore->data = dialed_interfaces; 04309 AST_LIST_HEAD_INIT(dialed_interfaces); 04310 04311 ast_channel_lock(qe->chan); 04312 ast_channel_datastore_add(qe->chan, datastore); 04313 ast_channel_unlock(qe->chan); 04314 } else 04315 dialed_interfaces = datastore->data; 04316 04317 AST_LIST_LOCK(dialed_interfaces); 04318 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 04319 if (!strcasecmp(cur->interface, di->interface)) { 04320 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 04321 di->interface); 04322 break; 04323 } 04324 } 04325 AST_LIST_UNLOCK(dialed_interfaces); 04326 04327 if (di) { 04328 callattempt_free(tmp); 04329 continue; 04330 } 04331 04332 /* It is always ok to dial a Local interface. We only keep track of 04333 * which "real" interfaces have been dialed. The Local channel will 04334 * inherit this list so that if it ends up dialing a real interface, 04335 * it won't call one that has already been called. */ 04336 if (strncasecmp(cur->interface, "Local/", 6)) { 04337 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 04338 ao2_ref(cur, -1); 04339 ao2_unlock(qe->parent); 04340 ao2_iterator_destroy(&memi); 04341 if (need_weight) 04342 ao2_unlock(queues); 04343 callattempt_free(tmp); 04344 goto out; 04345 } 04346 strcpy(di->interface, cur->interface); 04347 04348 AST_LIST_LOCK(dialed_interfaces); 04349 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 04350 AST_LIST_UNLOCK(dialed_interfaces); 04351 } 04352 04353 ast_channel_lock(qe->chan); 04354 /* 04355 * Seed the callattempt's connected line information with previously 04356 * acquired connected line info from the queued channel. The 04357 * previously acquired connected line info could have been set 04358 * through the CONNECTED_LINE dialplan function. 04359 */ 04360 ast_party_connected_line_copy(&tmp->connected, &qe->chan->connected); 04361 ast_channel_unlock(qe->chan); 04362 04363 tmp->stillgoing = -1; 04364 tmp->member = cur;/* Place the reference for cur into callattempt. */ 04365 tmp->lastcall = cur->lastcall; 04366 tmp->lastqueue = cur->lastqueue; 04367 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 04368 /* Special case: If we ring everyone, go ahead and ring them, otherwise 04369 just calculate their metric for the appropriate strategy */ 04370 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 04371 /* Put them in the list of outgoing thingies... We're ready now. 04372 XXX If we're forcibly removed, these outgoing calls won't get 04373 hung up XXX */ 04374 tmp->q_next = outgoing; 04375 outgoing = tmp; 04376 /* If this line is up, don't try anybody else */ 04377 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 04378 break; 04379 } else { 04380 callattempt_free(tmp); 04381 } 04382 } 04383 ao2_iterator_destroy(&memi); 04384 04385 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) { 04386 /* Application arguments have higher timeout priority (behaviour for <=1.6) */ 04387 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 04388 to = (qe->expire - now) * 1000; 04389 else 04390 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 04391 } else { 04392 /* Config timeout is higher priority thatn application timeout */ 04393 if (qe->expire && qe->expire<=now) { 04394 to = 0; 04395 } else if (qe->parent->timeout) { 04396 to = qe->parent->timeout * 1000; 04397 } else { 04398 to = -1; 04399 } 04400 } 04401 orig = to; 04402 ++qe->pending; 04403 ao2_unlock(qe->parent); 04404 ring_one(qe, outgoing, &numbusies); 04405 if (need_weight) 04406 ao2_unlock(queues); 04407 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed, update_connectedline); 04408 /* The ast_channel_datastore_remove() function could fail here if the 04409 * datastore was moved to another channel during a masquerade. If this is 04410 * the case, don't free the datastore here because later, when the channel 04411 * to which the datastore was moved hangs up, it will attempt to free this 04412 * datastore again, causing a crash 04413 */ 04414 ast_channel_lock(qe->chan); 04415 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 04416 ast_datastore_free(datastore); 04417 } 04418 ast_channel_unlock(qe->chan); 04419 ao2_lock(qe->parent); 04420 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { 04421 store_next_rr(qe, outgoing); 04422 } 04423 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) { 04424 store_next_lin(qe, outgoing); 04425 } 04426 ao2_unlock(qe->parent); 04427 peer = lpeer ? lpeer->chan : NULL; 04428 if (!peer) { 04429 qe->pending = 0; 04430 if (to) { 04431 /* Must gotten hung up */ 04432 res = -1; 04433 } else { 04434 /* User exited by pressing a digit */ 04435 res = digit; 04436 } 04437 if (res == -1) 04438 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name); 04439 if (ast_cdr_isset_unanswered()) { 04440 /* channel contains the name of one of the outgoing channels 04441 in its CDR; zero out this CDR to avoid a dual-posting */ 04442 struct callattempt *o; 04443 for (o = outgoing; o; o = o->q_next) { 04444 if (!o->chan) { 04445 continue; 04446 } 04447 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) { 04448 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED); 04449 break; 04450 } 04451 } 04452 } 04453 } else { /* peer is valid */ 04454 /* Ah ha! Someone answered within the desired timeframe. Of course after this 04455 we will always return with -1 so that it is hung up properly after the 04456 conversation. */ 04457 if (!strcmp(qe->chan->tech->type, "DAHDI")) 04458 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 04459 if (!strcmp(peer->tech->type, "DAHDI")) 04460 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 04461 /* Update parameters for the queue */ 04462 time(&now); 04463 recalc_holdtime(qe, (now - qe->start)); 04464 ao2_lock(qe->parent); 04465 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 04466 ao2_unlock(qe->parent); 04467 member = lpeer->member; 04468 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 04469 ao2_ref(member, 1); 04470 hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere); 04471 outgoing = NULL; 04472 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 04473 int res2; 04474 04475 res2 = ast_autoservice_start(qe->chan); 04476 if (!res2) { 04477 if (qe->parent->memberdelay) { 04478 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 04479 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 04480 } 04481 if (!res2 && announce) { 04482 play_file(peer, announce); 04483 } 04484 if (!res2 && qe->parent->reportholdtime) { 04485 if (!play_file(peer, qe->parent->sound_reporthold)) { 04486 int holdtime, holdtimesecs; 04487 04488 time(&now); 04489 holdtime = abs((now - qe->start) / 60); 04490 holdtimesecs = abs((now - qe->start) % 60); 04491 if (holdtime > 0) { 04492 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 04493 play_file(peer, qe->parent->sound_minutes); 04494 } 04495 if (holdtimesecs > 1) { 04496 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL); 04497 play_file(peer, qe->parent->sound_seconds); 04498 } 04499 } 04500 } 04501 } 04502 res2 |= ast_autoservice_stop(qe->chan); 04503 if (ast_check_hangup(peer)) { 04504 /* Agent must have hung up */ 04505 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 04506 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 04507 if (qe->parent->eventwhencalled) 04508 manager_event(EVENT_FLAG_AGENT, "AgentDump", 04509 "Queue: %s\r\n" 04510 "Uniqueid: %s\r\n" 04511 "Channel: %s\r\n" 04512 "Member: %s\r\n" 04513 "MemberName: %s\r\n" 04514 "%s", 04515 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 04516 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 04517 ast_hangup(peer); 04518 ao2_ref(member, -1); 04519 goto out; 04520 } else if (res2) { 04521 /* Caller must have hung up just before being connected*/ 04522 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 04523 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 04524 record_abandoned(qe); 04525 ast_hangup(peer); 04526 ao2_ref(member, -1); 04527 return -1; 04528 } 04529 } 04530 /* Stop music on hold */ 04531 if (ringing) 04532 ast_indicate(qe->chan,-1); 04533 else 04534 ast_moh_stop(qe->chan); 04535 /* If appropriate, log that we have a destination channel */ 04536 if (qe->chan->cdr) 04537 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 04538 /* Make sure channels are compatible */ 04539 res = ast_channel_make_compatible(qe->chan, peer); 04540 if (res < 0) { 04541 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 04542 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 04543 record_abandoned(qe); 04544 ast_cdr_failed(qe->chan->cdr); 04545 ast_hangup(peer); 04546 ao2_ref(member, -1); 04547 return -1; 04548 } 04549 04550 /* Play announcement to the caller telling it's his turn if defined */ 04551 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) { 04552 if (play_file(qe->chan, qe->parent->sound_callerannounce)) 04553 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce); 04554 } 04555 04556 ao2_lock(qe->parent); 04557 /* if setinterfacevar is defined, make member variables available to the channel */ 04558 /* use pbx_builtin_setvar to set a load of variables with one call */ 04559 if (qe->parent->setinterfacevar) { 04560 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d", 04561 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime); 04562 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 04563 pbx_builtin_setvar_multiple(peer, interfacevar); 04564 } 04565 04566 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */ 04567 /* use pbx_builtin_setvar to set a load of variables with one call */ 04568 if (qe->parent->setqueueentryvar) { 04569 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d", 04570 (long) time(NULL) - qe->start, qe->opos); 04571 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 04572 pbx_builtin_setvar_multiple(peer, interfacevar); 04573 } 04574 04575 ao2_unlock(qe->parent); 04576 04577 /* try to set queue variables if configured to do so*/ 04578 set_queue_variables(qe->parent, qe->chan); 04579 set_queue_variables(qe->parent, peer); 04580 04581 ast_channel_lock(qe->chan); 04582 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) { 04583 monitorfilename = ast_strdupa(monitorfilename); 04584 } 04585 ast_channel_unlock(qe->chan); 04586 /* Begin Monitoring */ 04587 if (qe->parent->monfmt && *qe->parent->monfmt) { 04588 if (!qe->parent->montype) { 04589 const char *monexec, *monargs; 04590 ast_debug(1, "Starting Monitor as requested.\n"); 04591 ast_channel_lock(qe->chan); 04592 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) { 04593 which = qe->chan; 04594 monexec = monexec ? ast_strdupa(monexec) : NULL; 04595 } 04596 else 04597 which = peer; 04598 ast_channel_unlock(qe->chan); 04599 if (monitorfilename) { 04600 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); 04601 } else if (qe->chan->cdr) { 04602 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); 04603 } else { 04604 /* Last ditch effort -- no CDR, make up something */ 04605 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 04606 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); 04607 } 04608 if (!ast_strlen_zero(monexec)) { 04609 ast_monitor_setjoinfiles(which, 1); 04610 } 04611 } else { 04612 mixmonapp = pbx_findapp("MixMonitor"); 04613 04614 if (mixmonapp) { 04615 ast_debug(1, "Starting MixMonitor as requested.\n"); 04616 if (!monitorfilename) { 04617 if (qe->chan->cdr) 04618 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)); 04619 else 04620 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 04621 } else { 04622 const char *m = monitorfilename; 04623 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) { 04624 switch (*m) { 04625 case '^': 04626 if (*(m + 1) == '{') 04627 *p = '$'; 04628 break; 04629 case ',': 04630 *p++ = '\\'; 04631 /* Fall through */ 04632 default: 04633 *p = *m; 04634 } 04635 if (*m == '\0') 04636 break; 04637 } 04638 if (p == tmpid2 + sizeof(tmpid2)) 04639 tmpid2[sizeof(tmpid2) - 1] = '\0'; 04640 04641 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 04642 } 04643 04644 ast_channel_lock(qe->chan); 04645 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) { 04646 monitor_exec = ast_strdupa(monitor_exec); 04647 } 04648 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) { 04649 monitor_options = ast_strdupa(monitor_options); 04650 } else { 04651 monitor_options = ""; 04652 } 04653 ast_channel_unlock(qe->chan); 04654 04655 if (monitor_exec) { 04656 const char *m = monitor_exec; 04657 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) { 04658 switch (*m) { 04659 case '^': 04660 if (*(m + 1) == '{') 04661 *p = '$'; 04662 break; 04663 case ',': 04664 *p++ = '\\'; 04665 /* Fall through */ 04666 default: 04667 *p = *m; 04668 } 04669 if (*m == '\0') 04670 break; 04671 } 04672 if (p == meid2 + sizeof(meid2)) 04673 meid2[sizeof(meid2) - 1] = '\0'; 04674 04675 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 04676 } 04677 04678 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt); 04679 04680 if (!ast_strlen_zero(monitor_exec)) 04681 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec); 04682 else 04683 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options); 04684 04685 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 04686 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 04687 if (qe->chan->cdr) 04688 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 04689 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 04690 if (qe->chan->cdr) 04691 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 04692 04693 } else { 04694 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 04695 } 04696 } 04697 } 04698 /* Drop out of the queue at this point, to prepare for next caller */ 04699 leave_queue(qe); 04700 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 04701 ast_debug(1, "app_queue: sendurl=%s.\n", url); 04702 ast_channel_sendurl(peer, url); 04703 } 04704 04705 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */ 04706 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */ 04707 if (!ast_strlen_zero(macro)) { 04708 macroexec = ast_strdupa(macro); 04709 } else { 04710 if (qe->parent->membermacro) 04711 macroexec = ast_strdupa(qe->parent->membermacro); 04712 } 04713 04714 if (!ast_strlen_zero(macroexec)) { 04715 ast_debug(1, "app_queue: macro=%s.\n", macroexec); 04716 04717 res = ast_autoservice_start(qe->chan); 04718 if (res) { 04719 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 04720 res = -1; 04721 } 04722 04723 application = pbx_findapp("Macro"); 04724 04725 if (application) { 04726 res = pbx_exec(peer, application, macroexec); 04727 ast_debug(1, "Macro exited with status %d\n", res); 04728 res = 0; 04729 } else { 04730 ast_log(LOG_ERROR, "Could not find application Macro\n"); 04731 res = -1; 04732 } 04733 04734 if (ast_autoservice_stop(qe->chan) < 0) { 04735 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 04736 res = -1; 04737 } 04738 } 04739 04740 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */ 04741 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */ 04742 if (!ast_strlen_zero(gosub)) { 04743 gosubexec = ast_strdupa(gosub); 04744 } else { 04745 if (qe->parent->membergosub) 04746 gosubexec = ast_strdupa(qe->parent->membergosub); 04747 } 04748 04749 if (!ast_strlen_zero(gosubexec)) { 04750 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec); 04751 04752 res = ast_autoservice_start(qe->chan); 04753 if (res) { 04754 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 04755 res = -1; 04756 } 04757 04758 application = pbx_findapp("Gosub"); 04759 04760 if (application) { 04761 char *gosub_args, *gosub_argstart; 04762 04763 /* Set where we came from */ 04764 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context)); 04765 ast_copy_string(peer->exten, "s", sizeof(peer->exten)); 04766 peer->priority = 0; 04767 04768 gosub_argstart = strchr(gosubexec, ','); 04769 if (gosub_argstart) { 04770 *gosub_argstart = 0; 04771 if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) { 04772 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 04773 gosub_args = NULL; 04774 } 04775 *gosub_argstart = ','; 04776 } else { 04777 if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) { 04778 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 04779 gosub_args = NULL; 04780 } 04781 } 04782 if (gosub_args) { 04783 res = pbx_exec(peer, application, gosub_args); 04784 if (!res) { 04785 struct ast_pbx_args args; 04786 memset(&args, 0, sizeof(args)); 04787 args.no_hangup_chan = 1; 04788 ast_pbx_run_args(peer, &args); 04789 } 04790 ast_free(gosub_args); 04791 ast_debug(1, "Gosub exited with status %d\n", res); 04792 } else { 04793 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); 04794 } 04795 } else { 04796 ast_log(LOG_ERROR, "Could not find application Gosub\n"); 04797 res = -1; 04798 } 04799 04800 if (ast_autoservice_stop(qe->chan) < 0) { 04801 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 04802 res = -1; 04803 } 04804 } 04805 04806 if (!ast_strlen_zero(agi)) { 04807 ast_debug(1, "app_queue: agi=%s.\n", agi); 04808 application = pbx_findapp("agi"); 04809 if (application) { 04810 agiexec = ast_strdupa(agi); 04811 ret = pbx_exec(qe->chan, application, agiexec); 04812 } else 04813 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 04814 } 04815 qe->handled++; 04816 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid, 04817 (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); 04818 if (update_cdr && qe->chan->cdr) 04819 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel)); 04820 if (qe->parent->eventwhencalled) 04821 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 04822 "Queue: %s\r\n" 04823 "Uniqueid: %s\r\n" 04824 "Channel: %s\r\n" 04825 "Member: %s\r\n" 04826 "MemberName: %s\r\n" 04827 "Holdtime: %ld\r\n" 04828 "BridgedChannel: %s\r\n" 04829 "Ringtime: %ld\r\n" 04830 "%s", 04831 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 04832 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0), 04833 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 04834 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 04835 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 04836 04837 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { 04838 queue_end_bridge->q = qe->parent; 04839 queue_end_bridge->chan = qe->chan; 04840 bridge_config.end_bridge_callback = end_bridge_callback; 04841 bridge_config.end_bridge_callback_data = queue_end_bridge; 04842 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; 04843 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need 04844 * to make sure to increase the refcount of this queue so it cannot be freed until we 04845 * are done with it. We remove this reference in end_bridge_callback. 04846 */ 04847 queue_t_ref(qe->parent, "For bridge_config reference"); 04848 } 04849 04850 time(&callstart); 04851 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 04852 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 04853 04854 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log 04855 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for 04856 * the AgentComplete manager event 04857 */ 04858 ast_channel_lock(qe->chan); 04859 if (!attended_transfer_occurred(qe->chan)) { 04860 struct ast_datastore *tds; 04861 04862 /* detect a blind transfer */ 04863 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) { 04864 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 04865 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 04866 (long) (time(NULL) - callstart), qe->opos); 04867 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 04868 } else if (ast_check_hangup(qe->chan)) { 04869 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 04870 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 04871 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER); 04872 } else { 04873 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 04874 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 04875 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT); 04876 } 04877 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 04878 ast_channel_datastore_remove(qe->chan, tds); 04879 } 04880 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 04881 } else { 04882 /* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */ 04883 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 04884 } 04885 04886 if (transfer_ds) { 04887 ast_datastore_free(transfer_ds); 04888 } 04889 ast_channel_unlock(qe->chan); 04890 ast_hangup(peer); 04891 res = bridge ? bridge : 1; 04892 ao2_ref(member, -1); 04893 } 04894 out: 04895 hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere); 04896 04897 return res; 04898 }
static int unload_module | ( | void | ) | [static] |
Definition at line 8070 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(), cli_queue, device_state_sub, devicestate_tps, extension_state_cb(), queue_t_unref, queueexists_function, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues_t_unlink, queuevar_function, and queuewaitingcount_function.
08071 { 08072 int res; 08073 struct ast_context *con; 08074 struct ao2_iterator q_iter; 08075 struct call_queue *q = NULL; 08076 08077 ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue)); 08078 res = ast_manager_unregister("QueueStatus"); 08079 res |= ast_manager_unregister("Queues"); 08080 res |= ast_manager_unregister("QueueRule"); 08081 res |= ast_manager_unregister("QueueSummary"); 08082 res |= ast_manager_unregister("QueueAdd"); 08083 res |= ast_manager_unregister("QueueRemove"); 08084 res |= ast_manager_unregister("QueuePause"); 08085 res |= ast_manager_unregister("QueueLog"); 08086 res |= ast_manager_unregister("QueuePenalty"); 08087 res |= ast_unregister_application(app_aqm); 08088 res |= ast_unregister_application(app_rqm); 08089 res |= ast_unregister_application(app_pqm); 08090 res |= ast_unregister_application(app_upqm); 08091 res |= ast_unregister_application(app_ql); 08092 res |= ast_unregister_application(app); 08093 res |= ast_custom_function_unregister(&queueexists_function); 08094 res |= ast_custom_function_unregister(&queuevar_function); 08095 res |= ast_custom_function_unregister(&queuemembercount_function); 08096 res |= ast_custom_function_unregister(&queuemembercount_dep); 08097 res |= ast_custom_function_unregister(&queuememberlist_function); 08098 res |= ast_custom_function_unregister(&queuewaitingcount_function); 08099 res |= ast_custom_function_unregister(&queuememberpenalty_function); 08100 08101 res |= ast_data_unregister(NULL); 08102 08103 if (device_state_sub) 08104 ast_event_unsubscribe(device_state_sub); 08105 08106 ast_extension_state_del(0, extension_state_cb); 08107 08108 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) { 08109 ast_context_remove_extension2(con, "s", 1, NULL, 0); 08110 ast_context_destroy(con, "app_queue"); /* leave no trace */ 08111 } 08112 08113 q_iter = ao2_iterator_init(queues, 0); 08114 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) { 08115 queues_t_unlink(queues, q, "Remove queue from container due to unload"); 08116 queue_t_unref(q, "Done with iterator"); 08117 } 08118 ao2_iterator_destroy(&q_iter); 08119 ao2_ref(queues, -1); 08120 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps); 08121 ast_unload_realtime("queue_members"); 08122 return res; 08123 }
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 3724 of file app_queue.c.
References ast_debug, AST_LIST_NEXT, queue_ent::chan, queue_ent::max_penalty, penalty_rule::max_relative, penalty_rule::max_value, queue_ent::min_penalty, penalty_rule::min_relative, penalty_rule::min_value, ast_channel::name, pbx_builtin_setvar_helper(), queue_ent::pr, and penalty_rule::time.
Referenced by queue_exec().
03725 { 03726 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value; 03727 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value; 03728 char max_penalty_str[20], min_penalty_str[20]; 03729 /* a relative change to the penalty could put it below 0 */ 03730 if (max_penalty < 0) 03731 max_penalty = 0; 03732 if (min_penalty < 0) 03733 min_penalty = 0; 03734 if (min_penalty > max_penalty) 03735 min_penalty = max_penalty; 03736 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty); 03737 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty); 03738 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str); 03739 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str); 03740 qe->max_penalty = max_penalty; 03741 qe->min_penalty = min_penalty; 03742 ast_debug(3, "Setting max penalty to %d and min penalty to %d for caller %s since %d seconds have elapsed\n", qe->max_penalty, qe->min_penalty, qe->chan->name, qe->pr->time); 03743 qe->pr = AST_LIST_NEXT(qe->pr, list); 03744 }
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 3832 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().
03833 { 03834 int oldtalktime; 03835 03836 struct member *mem; 03837 struct call_queue *qtmp; 03838 struct ao2_iterator queue_iter; 03839 03840 if (shared_lastcall) { 03841 queue_iter = ao2_iterator_init(queues, 0); 03842 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 03843 ao2_lock(qtmp); 03844 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { 03845 time(&mem->lastcall); 03846 mem->calls++; 03847 mem->lastqueue = q; 03848 ao2_ref(mem, -1); 03849 } 03850 ao2_unlock(qtmp); 03851 queue_t_unref(qtmp, "Done with iterator"); 03852 } 03853 ao2_iterator_destroy(&queue_iter); 03854 } else { 03855 ao2_lock(q); 03856 time(&member->lastcall); 03857 member->calls++; 03858 member->lastqueue = q; 03859 ao2_unlock(q); 03860 } 03861 ao2_lock(q); 03862 q->callscompleted++; 03863 if (callcompletedinsl) 03864 q->callscompletedinsl++; 03865 /* Calculate talktime using the same exponential average as holdtime code*/ 03866 oldtalktime = q->talktime; 03867 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2; 03868 ao2_unlock(q); 03869 return 0; 03870 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 2228 of file app_queue.c.
References ast_strlen_zero(), ast_update_realtime(), member::rt_uniqueid, and SENTINEL.
Referenced by set_member_paused().
02229 { 02230 int ret = -1; 02231 02232 if (ast_strlen_zero(mem->rt_uniqueid)) 02233 return ret; 02234 02235 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) 02236 ret = 0; 02237 02238 return ret; 02239 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 2242 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, queues, member::realtime, rt_handle_member_record(), S_OR, and SENTINEL.
Referenced by load_realtime_queue(), and queue_exec().
02243 { 02244 struct ast_config *member_config = NULL; 02245 struct member *m; 02246 char *interface = NULL; 02247 struct ao2_iterator mem_iter; 02248 02249 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) { 02250 /*This queue doesn't have realtime members*/ 02251 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name); 02252 return; 02253 } 02254 02255 ao2_lock(queues); 02256 ao2_lock(q); 02257 02258 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 02259 mem_iter = ao2_iterator_init(q->members, 0); 02260 while ((m = ao2_iterator_next(&mem_iter))) { 02261 if (m->realtime) 02262 m->dead = 1; 02263 ao2_ref(m, -1); 02264 } 02265 ao2_iterator_destroy(&mem_iter); 02266 02267 while ((interface = ast_category_browse(member_config, interface))) { 02268 rt_handle_member_record(q, interface, 02269 ast_variable_retrieve(member_config, interface, "uniqueid"), 02270 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 02271 ast_variable_retrieve(member_config, interface, "penalty"), 02272 ast_variable_retrieve(member_config, interface, "paused"), 02273 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface)); 02274 } 02275 02276 /* Delete all realtime members that have been deleted in DB. */ 02277 mem_iter = ao2_iterator_init(q->members, 0); 02278 while ((m = ao2_iterator_next(&mem_iter))) { 02279 if (m->dead) { 02280 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 02281 ao2_unlink(q->members, m); 02282 q->membercount--; 02283 } 02284 ao2_ref(m, -1); 02285 } 02286 ao2_iterator_destroy(&mem_iter); 02287 ao2_unlock(q); 02288 ao2_unlock(queues); 02289 ast_config_destroy(member_config); 02290 }
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 1267 of file app_queue.c.
References EVENT_FLAG_AGENT, and manager_event.
Referenced by extension_state_cb(), handle_statechange(), and ring_entry().
01268 { 01269 m->status = status; 01270 01271 if (q->maskmemberstatus) 01272 return 0; 01273 01274 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 01275 "Queue: %s\r\n" 01276 "Location: %s\r\n" 01277 "MemberName: %s\r\n" 01278 "Membership: %s\r\n" 01279 "Penalty: %d\r\n" 01280 "CallsTaken: %d\r\n" 01281 "LastCall: %d\r\n" 01282 "Status: %d\r\n" 01283 "Paused: %d\r\n", 01284 q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static", 01285 m->penalty, m->calls, (int)m->lastcall, m->status, m->paused 01286 ); 01287 01288 return 0; 01289 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
UnPauseQueueMember application.
Definition at line 5383 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().
05384 { 05385 char *parse; 05386 AST_DECLARE_APP_ARGS(args, 05387 AST_APP_ARG(queuename); 05388 AST_APP_ARG(interface); 05389 AST_APP_ARG(options); 05390 AST_APP_ARG(reason); 05391 ); 05392 05393 if (ast_strlen_zero(data)) { 05394 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n"); 05395 return -1; 05396 } 05397 05398 parse = ast_strdupa(data); 05399 05400 AST_STANDARD_APP_ARGS(args, parse); 05401 05402 if (ast_strlen_zero(args.interface)) { 05403 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 05404 return -1; 05405 } 05406 05407 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) { 05408 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 05409 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 05410 return 0; 05411 } 05412 05413 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 05414 05415 return 0; 05416 }
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 2398 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(), and wait_a_bit().
02399 { 02400 int digitlen = strlen(qe->digits); 02401 02402 /* Prevent possible buffer overflow */ 02403 if (digitlen < sizeof(qe->digits) - 2) { 02404 qe->digits[digitlen] = digit; 02405 qe->digits[digitlen + 1] = '\0'; 02406 } else { 02407 qe->digits[0] = '\0'; 02408 return 0; 02409 } 02410 02411 /* If there's no context to goto, short-circuit */ 02412 if (ast_strlen_zero(qe->context)) 02413 return 0; 02414 02415 /* If the extension is bad, then reset the digits to blank */ 02416 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, 02417 S_COR(qe->chan->caller.id.number.valid, qe->chan->caller.id.number.str, NULL))) { 02418 qe->digits[0] = '\0'; 02419 return 0; 02420 } 02421 02422 /* We have an exact match */ 02423 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 02424 qe->valid_digits = 1; 02425 /* Return 1 on a successful goto */ 02426 return 1; 02427 } 02428 02429 return 0; 02430 }
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 2788 of file app_queue.c.
References ast_copy_string(), ast_str_buffer(), ast_str_thread_get(), queue_ent::chan, and pbx_builtin_serialize_variables().
Referenced by ring_entry(), rna(), and send_agent_complete().
02789 { 02790 struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1); 02791 const char *tmp; 02792 02793 if (pbx_builtin_serialize_variables(chan, &buf)) { 02794 int i, j; 02795 02796 /* convert "\n" to "\nVariable: " */ 02797 strcpy(vars, "Variable: "); 02798 tmp = ast_str_buffer(buf); 02799 02800 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 02801 vars[j] = tmp[i]; 02802 02803 if (tmp[i + 1] == '\0') 02804 break; 02805 if (tmp[i] == '\n') { 02806 vars[j++] = '\r'; 02807 vars[j++] = '\n'; 02808 02809 ast_copy_string(&(vars[j]), "Variable: ", len - j); 02810 j += 9; 02811 } 02812 } 02813 if (j > len - 3) 02814 j = len - 3; 02815 vars[j++] = '\r'; 02816 vars[j++] = '\n'; 02817 vars[j] = '\0'; 02818 } else { 02819 /* there are no channel variables; leave it blank */ 02820 *vars = '\0'; 02821 } 02822 return vars; 02823 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 4900 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
04901 { 04902 /* Don't need to hold the lock while we setup the outgoing calls */ 04903 int retrywait = qe->parent->retry * 1000; 04904 04905 int res = ast_waitfordigit(qe->chan, retrywait); 04906 if (res > 0 && !valid_exit(qe, res)) 04907 res = 0; 04908 04909 return res; 04910 }
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 | update_connectedline | |||
) | [static] |
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() |
[in] | update_connectedline | Allow connected line and redirecting updates to pass through. |
Definition at line 3264 of file app_queue.c.
References ast_channel_lock, ast_channel_unlock, AST_MAX_WATCHERS, ast_party_connected_line_init(), ast_poll_channel_add(), ast_strdupa, callattempt::call_next, callattempt::chan, queue_ent::chan, f, ast_channel::name, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, status, callattempt::stillgoing, and call_queue::strategy.
03265 { 03266 const char *queue = qe->parent->name; 03267 struct callattempt *o, *start = NULL, *prev = NULL; 03268 int res; 03269 int status; 03270 int numbusies = prebusies; 03271 int numnochan = 0; 03272 int stillgoing = 0; 03273 int orig = *to; 03274 struct ast_frame *f; 03275 struct callattempt *peer = NULL; 03276 struct ast_channel *winner; 03277 struct ast_channel *in = qe->chan; 03278 char on[80] = ""; 03279 char membername[80] = ""; 03280 long starttime = 0; 03281 long endtime = 0; 03282 #ifdef HAVE_EPOLL 03283 struct callattempt *epollo; 03284 #endif 03285 struct ast_party_connected_line connected_caller; 03286 char *inchan_name; 03287 03288 ast_party_connected_line_init(&connected_caller); 03289 03290 ast_channel_lock(qe->chan); 03291 inchan_name = ast_strdupa(qe->chan->name); 03292 ast_channel_unlock(qe->chan); 03293 03294 starttime = (long) time(NULL); 03295 #ifdef HAVE_EPOLL 03296 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 03297 if (epollo->chan) 03298 ast_poll_channel_add(in, epollo->chan); 03299 } 03300 #endif 03301 03302 while (*to && !peer) { 03303 int numlines, retry, pos = 1; 03304 struct ast_channel *watchers[AST_MAX_WATCHERS]; 03305 watchers[0] = in; 03306 start = NULL; 03307 03308 for (retry = 0; retry < 2; retry++) { 03309 numlines = 0; 03310 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 03311 if (o->stillgoing) { /* Keep track of important channels */ 03312 stillgoing = 1; 03313 if (o->chan) { 03314 watchers[pos++] = o->chan; 03315 if (!start) 03316 start = o; 03317 else 03318 prev->call_next = o; 03319 prev = o; 03320 } 03321 } 03322 numlines++; 03323 } 03324 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 03325 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 03326 break; 03327 /* On "ringall" strategy we only move to the next penalty level 03328 when *all* ringing phones are done in the current penalty level */ 03329 ring_one(qe, outgoing, &numbusies); 03330 /* and retry... */ 03331 } 03332 if (pos == 1 /* not found */) { 03333 if (numlines == (numbusies + numnochan)) { 03334 ast_debug(1, "Everyone is busy at this time\n"); 03335 } else { 03336 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan); 03337 } 03338 *to = 0; 03339 return NULL; 03340 } 03341 03342 /* Poll for events from both the incoming channel as well as any outgoing channels */ 03343 winner = ast_waitfor_n(watchers, pos, to); 03344 03345 /* Service all of the outgoing channels */ 03346 for (o = start; o; o = o->call_next) { 03347 /* We go with a static buffer here instead of using ast_strdupa. Using 03348 * ast_strdupa in a loop like this one can cause a stack overflow 03349 */ 03350 char ochan_name[AST_CHANNEL_NAME]; 03351 if (o->chan) { 03352 ast_channel_lock(o->chan); 03353 ast_copy_string(ochan_name, o->chan->name, sizeof(ochan_name)); 03354 ast_channel_unlock(o->chan); 03355 } 03356 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 03357 if (!peer) { 03358 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 03359 if (update_connectedline) { 03360 if (o->pending_connected_update) { 03361 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { 03362 ast_channel_update_connected_line(in, &o->connected, NULL); 03363 } 03364 } else if (!o->dial_callerid_absent) { 03365 ast_channel_lock(o->chan); 03366 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); 03367 ast_channel_unlock(o->chan); 03368 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; 03369 ast_channel_update_connected_line(in, &connected_caller, NULL); 03370 ast_party_connected_line_free(&connected_caller); 03371 } 03372 } 03373 if (o->aoc_s_rate_list) { 03374 size_t encoded_size; 03375 struct ast_aoc_encoded *encoded; 03376 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) { 03377 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size); 03378 ast_aoc_destroy_encoded(encoded); 03379 } 03380 } 03381 peer = o; 03382 } 03383 } else if (o->chan && (o->chan == winner)) { 03384 03385 ast_copy_string(on, o->member->interface, sizeof(on)); 03386 ast_copy_string(membername, o->member->membername, sizeof(membername)); 03387 03388 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 03389 ast_verb(3, "Forwarding %s to '%s' prevented.\n", inchan_name, o->chan->call_forward); 03390 numnochan++; 03391 do_hang(o); 03392 winner = NULL; 03393 continue; 03394 } else if (!ast_strlen_zero(o->chan->call_forward)) { 03395 struct ast_channel *original = o->chan; 03396 char tmpchan[256]; 03397 char *stuff; 03398 char *tech; 03399 03400 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 03401 if ((stuff = strchr(tmpchan, '/'))) { 03402 *stuff++ = '\0'; 03403 tech = tmpchan; 03404 } else { 03405 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 03406 stuff = tmpchan; 03407 tech = "Local"; 03408 } 03409 03410 ast_cel_report_event(in, AST_CEL_FORWARD, NULL, o->chan->call_forward, NULL); 03411 03412 /* Before processing channel, go ahead and check for forwarding */ 03413 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", inchan_name, tech, stuff, ochan_name); 03414 /* Setup parameters */ 03415 o->chan = ast_request(tech, in->nativeformats, in, stuff, &status); 03416 if (!o->chan) { 03417 ast_log(LOG_NOTICE, 03418 "Forwarding failed to create channel to dial '%s/%s'\n", 03419 tech, stuff); 03420 o->stillgoing = 0; 03421 numnochan++; 03422 } else { 03423 struct ast_party_redirecting redirecting; 03424 03425 ast_channel_lock(o->chan); 03426 while (ast_channel_trylock(in)) { 03427 CHANNEL_DEADLOCK_AVOIDANCE(o->chan); 03428 } 03429 ast_channel_inherit_variables(in, o->chan); 03430 ast_channel_datastore_inherit(in, o->chan); 03431 03432 ast_string_field_set(o->chan, accountcode, in->accountcode); 03433 03434 ast_channel_set_redirecting(o->chan, &original->redirecting, NULL); 03435 if (!o->chan->redirecting.from.number.valid 03436 || ast_strlen_zero(o->chan->redirecting.from.number.str)) { 03437 /* 03438 * The call was not previously redirected so it is 03439 * now redirected from this number. 03440 */ 03441 ast_party_number_free(&o->chan->redirecting.from.number); 03442 ast_party_number_init(&o->chan->redirecting.from.number); 03443 o->chan->redirecting.from.number.valid = 1; 03444 o->chan->redirecting.from.number.str = 03445 ast_strdup(S_OR(in->macroexten, in->exten)); 03446 } 03447 03448 o->chan->dialed.transit_network_select = in->dialed.transit_network_select; 03449 03450 ast_party_caller_copy(&o->chan->caller, &in->caller); 03451 ast_party_connected_line_copy(&o->chan->connected, &original->connected); 03452 03453 /* 03454 * We must unlock o->chan before calling 03455 * ast_channel_redirecting_macro, because we put o->chan into 03456 * autoservice there. That is pretty much a guaranteed 03457 * deadlock. This is why the handling of o->chan's lock may 03458 * seem a bit unusual here. 03459 */ 03460 ast_party_redirecting_init(&redirecting); 03461 ast_party_redirecting_copy(&redirecting, &o->chan->redirecting); 03462 ast_channel_unlock(o->chan); 03463 res = ast_channel_redirecting_macro(o->chan, in, &redirecting, 1, 0); 03464 if (res) { 03465 ast_channel_update_redirecting(in, &redirecting, NULL); 03466 } 03467 ast_party_redirecting_free(&redirecting); 03468 ast_channel_unlock(in); 03469 03470 update_connectedline = 1; 03471 03472 if (ast_call(o->chan, stuff, 0)) { 03473 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", 03474 tech, stuff); 03475 do_hang(o); 03476 numnochan++; 03477 } 03478 } 03479 /* Hangup the original channel now, in case we needed it */ 03480 ast_hangup(winner); 03481 continue; 03482 } 03483 f = ast_read(winner); 03484 if (f) { 03485 if (f->frametype == AST_FRAME_CONTROL) { 03486 switch (f->subclass.integer) { 03487 case AST_CONTROL_ANSWER: 03488 /* This is our guy if someone answered. */ 03489 if (!peer) { 03490 ast_verb(3, "%s answered %s\n", ochan_name, inchan_name); 03491 if (update_connectedline) { 03492 if (o->pending_connected_update) { 03493 if (ast_channel_connected_line_macro(o->chan, in, &o->connected, 1, 0)) { 03494 ast_channel_update_connected_line(in, &o->connected, NULL); 03495 } 03496 } else if (!o->dial_callerid_absent) { 03497 ast_channel_lock(o->chan); 03498 ast_connected_line_copy_from_caller(&connected_caller, &o->chan->caller); 03499 ast_channel_unlock(o->chan); 03500 connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; 03501 ast_channel_update_connected_line(in, &connected_caller, NULL); 03502 ast_party_connected_line_free(&connected_caller); 03503 } 03504 } 03505 if (o->aoc_s_rate_list) { 03506 size_t encoded_size; 03507 struct ast_aoc_encoded *encoded; 03508 if ((encoded = ast_aoc_encode(o->aoc_s_rate_list, &encoded_size, o->chan))) { 03509 ast_indicate_data(in, AST_CONTROL_AOC, encoded, encoded_size); 03510 ast_aoc_destroy_encoded(encoded); 03511 } 03512 } 03513 peer = o; 03514 } 03515 break; 03516 case AST_CONTROL_BUSY: 03517 ast_verb(3, "%s is busy\n", ochan_name); 03518 if (in->cdr) 03519 ast_cdr_busy(in->cdr); 03520 do_hang(o); 03521 endtime = (long) time(NULL); 03522 endtime -= starttime; 03523 rna(endtime * 1000, qe, on, membername, 0); 03524 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03525 if (qe->parent->timeoutrestart) 03526 *to = orig; 03527 /* Have enough time for a queue member to answer? */ 03528 if (*to > 500) { 03529 ring_one(qe, outgoing, &numbusies); 03530 starttime = (long) time(NULL); 03531 } 03532 } 03533 numbusies++; 03534 break; 03535 case AST_CONTROL_CONGESTION: 03536 ast_verb(3, "%s is circuit-busy\n", ochan_name); 03537 if (in->cdr) 03538 ast_cdr_busy(in->cdr); 03539 endtime = (long) time(NULL); 03540 endtime -= starttime; 03541 rna(endtime * 1000, qe, on, membername, 0); 03542 do_hang(o); 03543 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03544 if (qe->parent->timeoutrestart) 03545 *to = orig; 03546 if (*to > 500) { 03547 ring_one(qe, outgoing, &numbusies); 03548 starttime = (long) time(NULL); 03549 } 03550 } 03551 numbusies++; 03552 break; 03553 case AST_CONTROL_RINGING: 03554 ast_verb(3, "%s is ringing\n", ochan_name); 03555 03556 /* Start ring indication when the channel is ringing, if specified */ 03557 if (qe->ring_when_ringing) { 03558 ast_moh_stop(qe->chan); 03559 ast_indicate(qe->chan, AST_CONTROL_RINGING); 03560 } 03561 break; 03562 case AST_CONTROL_OFFHOOK: 03563 /* Ignore going off hook */ 03564 break; 03565 case AST_CONTROL_CONNECTED_LINE: 03566 if (!update_connectedline) { 03567 ast_verb(3, "Connected line update to %s prevented.\n", inchan_name); 03568 } else if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 03569 struct ast_party_connected_line connected; 03570 ast_verb(3, "%s connected line has changed. Saving it until answer for %s\n", ochan_name, inchan_name); 03571 ast_party_connected_line_set_init(&connected, &o->connected); 03572 ast_connected_line_parse_data(f->data.ptr, f->datalen, &connected); 03573 ast_party_connected_line_set(&o->connected, &connected, NULL); 03574 ast_party_connected_line_free(&connected); 03575 o->pending_connected_update = 1; 03576 } else { 03577 if (ast_channel_connected_line_macro(o->chan, in, f, 1, 1)) { 03578 ast_indicate_data(in, AST_CONTROL_CONNECTED_LINE, f->data.ptr, f->datalen); 03579 } 03580 } 03581 break; 03582 case AST_CONTROL_AOC: 03583 { 03584 struct ast_aoc_decoded *decoded = ast_aoc_decode(f->data.ptr, f->datalen, o->chan); 03585 if (decoded && (ast_aoc_get_msg_type(decoded) == AST_AOC_S)) { 03586 ast_aoc_destroy_decoded(o->aoc_s_rate_list); 03587 o->aoc_s_rate_list = decoded; 03588 } else { 03589 ast_aoc_destroy_decoded(decoded); 03590 } 03591 } 03592 break; 03593 case AST_CONTROL_REDIRECTING: 03594 if (!update_connectedline) { 03595 ast_verb(3, "Redirecting update to %s prevented\n", inchan_name); 03596 } else { 03597 ast_verb(3, "%s redirecting info has changed, passing it to %s\n", ochan_name, inchan_name); 03598 if (ast_channel_redirecting_macro(o->chan, in, f, 1, 1)) { 03599 ast_indicate_data(in, AST_CONTROL_REDIRECTING, f->data.ptr, f->datalen); 03600 } 03601 } 03602 break; 03603 default: 03604 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass.integer); 03605 break; 03606 } 03607 } 03608 ast_frfree(f); 03609 } else { /* ast_read() returned NULL */ 03610 endtime = (long) time(NULL) - starttime; 03611 rna(endtime * 1000, qe, on, membername, 1); 03612 do_hang(o); 03613 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03614 if (qe->parent->timeoutrestart) 03615 *to = orig; 03616 if (*to > 500) { 03617 ring_one(qe, outgoing, &numbusies); 03618 starttime = (long) time(NULL); 03619 } 03620 } 03621 } 03622 } 03623 } 03624 03625 /* If we received an event from the caller, deal with it. */ 03626 if (winner == in) { 03627 f = ast_read(in); 03628 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass.integer == AST_CONTROL_HANGUP))) { 03629 /* Got hung up */ 03630 *to = -1; 03631 if (f) { 03632 if (f->data.uint32) { 03633 in->hangupcause = f->data.uint32; 03634 } 03635 ast_frfree(f); 03636 } 03637 return NULL; 03638 } 03639 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass.integer == '*')) { 03640 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass.integer); 03641 *to = 0; 03642 ast_frfree(f); 03643 return NULL; 03644 } 03645 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass.integer)) { 03646 ast_verb(3, "User pressed digit: %c\n", f->subclass.integer); 03647 *to = 0; 03648 *digit = f->subclass.integer; 03649 ast_frfree(f); 03650 return NULL; 03651 } 03652 ast_frfree(f); 03653 } 03654 if (!*to) { 03655 for (o = start; o; o = o->call_next) 03656 rna(orig, qe, o->interface, o->member->membername, 1); 03657 } 03658 } 03659 03660 #ifdef HAVE_EPOLL 03661 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 03662 if (epollo->chan) 03663 ast_poll_channel_del(in, epollo->chan); 03664 } 03665 #endif 03666 03667 return peer; 03668 }
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 3756 of file app_queue.c.
References ast_queue_log(), queue_ent::chan, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, queue_ent::min_penalty, call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, QUEUE_LEAVEEMPTY, QUEUE_TIMEOUT, queue_ent::start, status, and ast_channel::uniqueid.
Referenced by queue_exec().
03757 { 03758 int res = 0; 03759 03760 /* This is the holding pen for callers 2 through maxlen */ 03761 for (;;) { 03762 03763 if (is_our_turn(qe)) 03764 break; 03765 03766 /* If we have timed out, break out */ 03767 if (qe->expire && (time(NULL) >= qe->expire)) { 03768 *reason = QUEUE_TIMEOUT; 03769 break; 03770 } 03771 03772 if (qe->parent->leavewhenempty) { 03773 int status = 0; 03774 03775 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) { 03776 *reason = QUEUE_LEAVEEMPTY; 03777 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 03778 leave_queue(qe); 03779 break; 03780 } 03781 } 03782 03783 /* Make a position announcement, if enabled */ 03784 if (qe->parent->announcefrequency && 03785 (res = say_position(qe,ringing))) 03786 break; 03787 03788 /* If we have timed out, break out */ 03789 if (qe->expire && (time(NULL) >= qe->expire)) { 03790 *reason = QUEUE_TIMEOUT; 03791 break; 03792 } 03793 03794 /* Make a periodic announcement, if enabled */ 03795 if (qe->parent->periodicannouncefrequency && 03796 (res = say_periodic_announcement(qe,ringing))) 03797 break; 03798 03799 /* see if we need to move to the next penalty level for this queue */ 03800 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) { 03801 update_qe_rule(qe); 03802 } 03803 03804 /* If we have timed out, break out */ 03805 if (qe->expire && (time(NULL) >= qe->expire)) { 03806 *reason = QUEUE_TIMEOUT; 03807 break; 03808 } 03809 03810 /* Wait a second before checking again */ 03811 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 03812 if (res > 0 && !valid_exit(qe, res)) 03813 res = 0; 03814 else 03815 break; 03816 } 03817 03818 /* If we have timed out, break out */ 03819 if (qe->expire && (time(NULL) >= qe->expire)) { 03820 *reason = QUEUE_TIMEOUT; 03821 break; 03822 } 03823 } 03824 03825 return res; 03826 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_DEVSTATE_CONSUMER, .nonoptreq = "res_monitor", } [static] |
Definition at line 8205 of file app_queue.c.
char* app = "Queue" [static] |
Definition at line 739 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 741 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 745 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 749 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 743 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 747 of file app_queue.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 8205 of file app_queue.c.
int autofill_default = 1 [static] |
struct autopause autopausesmodes[] [static] |
Referenced by autopause2int().
struct ast_cli_entry cli_queue[] [static] |
struct ast_event_sub* device_state_sub [static] |
Subscription to device state change events.
Definition at line 772 of file app_queue.c.
Referenced by aji_init_event_distribution(), load_module(), load_pbx(), and unload_module().
struct ast_taskprocessor* devicestate_tps [static] |
Definition at line 723 of file app_queue.c.
Referenced by device_state_cb(), load_module(), and unload_module().
enum queue_result id |
Definition at line 789 of file app_queue.c.
Referenced by _sip_show_peers(), _skinny_show_devices(), _skinny_show_lines(), amixer_max(), ast_cc_extension_monitor_add_dialstring(), ast_party_id_presentation(), frame_trace_helper(), idemodulator(), misdn_queue_connected_line_update(), misdn_update_caller_id(), party_id_build_data(), party_id_read(), party_id_write(), pidf_validate_tuple(), and setamixer().
int montype_default = 0 [static] |
const char* const pm_family = "Queue/PersistentMembers" [static] |
const char qpm_cmd_usage[] [static] |
Initial value:
"Usage: queue pause member <channel> in <queue> reason <reason>\n"
Definition at line 7784 of file app_queue.c.
const char qsmp_cmd_usage[] [static] |
Initial value:
"Usage: queue set member penalty <channel> from <queue> <penalty>\n"
Definition at line 7790 of file app_queue.c.
struct ast_data_entry queue_data_providers[] [static] |
Initial value:
{ AST_DATA_ENTRY("asterisk/application/queue/list", &queues_data_provider), }
Definition at line 8066 of file app_queue.c.
Referenced by load_module().
int queue_persistent_members = 0 [static] |
struct { ... } queue_results[] [static] |
Referenced by set_queue_result().
struct ast_datastore_info queue_transfer_info [static] |
Initial value:
{ .type = "queue_transfer", .chan_fixup = queue_transfer_fixup, .destroy = queue_transfer_destroy, }
Definition at line 4005 of file app_queue.c.
Referenced by attended_transfer_occurred(), queue_transfer_fixup(), and setup_transfer_datastore().
struct ast_custom_function queueexists_function [static] |
Initial value:
{ .name = "QUEUE_EXISTS", .read = queue_function_exists, }
Definition at line 6242 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuemembercount_dep [static] |
Initial value:
{ .name = "QUEUE_MEMBER_COUNT", .read = queue_function_qac_dep, }
Definition at line 6257 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuemembercount_function [static] |
Initial value:
{ .name = "QUEUE_MEMBER", .read = queue_function_qac, }
Definition at line 6252 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberlist_function [static] |
Initial value:
{ .name = "QUEUE_MEMBER_LIST", .read = queue_function_queuememberlist, }
Definition at line 6267 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberpenalty_function [static] |
Initial value:
{ .name = "QUEUE_MEMBER_PENALTY", .read = queue_function_memberpenalty_read, .write = queue_function_memberpenalty_write, }
Definition at line 6272 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ao2_container* queues [static] |
Definition at line 1030 of file app_queue.c.
Referenced by compare_weight(), extension_state_cb(), find_queue_by_name_rt(), handle_statechange(), join_queue(), leave_queue(), load_realtime_queue(), update_queue(), and update_realtime_members().
struct ast_data_handler queues_data_provider [static] |
Initial value:
{ .version = AST_DATA_HANDLER_VERSION, .get = queues_data_provider_get }
Definition at line 8061 of file app_queue.c.
struct ast_custom_function queuevar_function [static] |
Initial value:
{ .name = "QUEUE_VARIABLES", .read = queue_function_var, }
Definition at line 6247 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuewaitingcount_function [static] |
Initial value:
{ .name = "QUEUE_WAITING_COUNT", .read = queue_function_queuewaitingcount, }
Definition at line 6262 of file app_queue.c.
Referenced by load_module(), and unload_module().
const char qum_cmd_usage[] [static] |
Initial value:
"Usage: queue unpause member <channel> in <queue> reason <reason>\n"
Definition at line 7787 of file app_queue.c.
int shared_lastcall = 1 [static] |
struct strategy strategies[] [static] |
Referenced by int2strat(), and strat2int().
char* text |
Definition at line 790 of file app_queue.c.
Referenced by festival_exec(), iconv_read(), method_match(), process_sdp(), reqprep(), set_queue_result(), and sip_new().
int update_cdr = 0 [static] |
queues.conf [general] option
Definition at line 775 of file app_queue.c.
Referenced by login_exec().
int use_weight = 0 [static] |