#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"
Go to the source code of this file.
Data Structures | |
struct | call_queue |
struct | callattempt |
We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More... | |
struct | interfaces |
struct | member |
struct | member_interface |
struct | penalty_rule |
struct | queue_end_bridge |
struct | queue_ent |
struct | queue_transfer_ds |
struct | rule_list |
struct | rule_lists |
struct | statechange |
struct | strategy |
Defines | |
#define | ANNOUNCEHOLDTIME_ALWAYS 1 |
#define | ANNOUNCEHOLDTIME_ONCE 2 |
#define | ANNOUNCEPOSITION_LIMIT 4 |
#define | ANNOUNCEPOSITION_MORE_THAN 3 |
#define | ANNOUNCEPOSITION_NO 2 |
#define | ANNOUNCEPOSITION_YES 1 |
#define | AST_MAX_WATCHERS 256 |
#define | DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 |
#define | DEFAULT_RETRY 5 |
#define | DEFAULT_TIMEOUT 15 |
#define | MAX_PERIODIC_ANNOUNCEMENTS 10 |
#define | MAX_QUEUE_BUCKETS 53 |
#define | PM_MAX_LEN 8192 |
#define | QUEUE_EMPTY_LOOSE 3 |
#define | QUEUE_EMPTY_NORMAL 1 |
#define | QUEUE_EMPTY_STRICT 2 |
#define | QUEUE_EVENT_VARIABLES 3 |
#define | RECHECK 1 |
#define | RES_EXISTS (-1) |
#define | RES_NOSUCHQUEUE (-3) |
#define | RES_NOT_DYNAMIC (-4) |
#define | RES_OKAY 0 |
#define | RES_OUTOFMEMORY (-2) |
Enumerations | |
enum | { QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM } |
enum | agent_complete_reason { CALLER, AGENT, TRANSFER } |
enum | queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_NORMAL } |
enum | queue_result { QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3, QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7 } |
enum | queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF } |
Functions | |
static char * | __queues_show (struct mansession *s, int fd, int argc, char **argv) |
Show queue(s) status and statistics. | |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | add_to_interfaces (const char *interface) |
static int | add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface) |
Add member to queue. | |
static struct call_queue * | alloc_queue (const char *queuename) |
static int | aqm_exec (struct ast_channel *chan, void *data) |
AddQueueMember application. | |
static int | attended_transfer_occurred (struct ast_channel *chan) |
mechanism to tell if a queue caller was atxferred by a queue member. | |
static int | calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp) |
Calculate the metric of each member in the outgoing callattempts. | |
static void | clear_and_free_interfaces (void) |
static void | clear_queue (struct call_queue *q) |
static int | compare_weight (struct call_queue *rq, struct member *member) |
static char * | complete_queue (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_add_member (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_pause_member (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_remove_member (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_rule_show (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state) |
static char * | complete_queue_show (const char *line, const char *word, int pos, int state) |
static int | compress_char (const char c) |
static void | copy_rules (struct queue_ent *qe, const char *rulename) |
Copy rule from global list into specified queue. | |
static struct 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 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 enum queue_member_status | get_member_status (struct call_queue *q, int max_penalty, int min_penalty) |
Check if members are available. | |
static char * | handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_rule_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static char * | handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static int | handle_statechange (void *datap) |
set a member's status based on device state of that member's interface | |
static void | hangupcalls (struct callattempt *outgoing, struct ast_channel *exception) |
Hang up a list of outgoing calls. | |
static void | init_queue (struct call_queue *q) |
Initialize Queue default values. | |
static void | insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos) |
Insert the 'new' entry after the 'prev' entry of queue 'q'. | |
static int | insert_penaltychange (const char *list_name, const char *content, const int linenum) |
Change queue penalty by adding rule. | |
static const char * | int2strat (int strategy) |
static struct member * | interface_exists (struct call_queue *q, const char *interface) |
static int | interface_exists_global (const char *interface, int lock_queue_container) |
static int | is_our_turn (struct queue_ent *qe) |
Check if we should start attempting to call queue members. | |
static int | join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason, const char *overriding_rule) |
static void | leave_queue (struct queue_ent *qe) |
Caller leaving queue. | |
static int | load_module (void) |
static struct call_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_rule_show (struct mansession *s, const struct message *m) |
static int | manager_queues_show (struct mansession *s, const struct message *m) |
static int | manager_queues_status (struct mansession *s, const struct message *m) |
Queue status info via AMI. | |
static int | manager_queues_summary (struct mansession *s, const struct message *m) |
Summary of queue info via the AMI. | |
static int | manager_remove_queue_member (struct mansession *s, const struct message *m) |
static int | member_cmp_fn (void *obj1, void *obj2, int flags) |
static int | member_hash_fn (const void *obj, const int flags) |
static int | num_available_members (struct call_queue *q) |
Get the number of members available to accept a call. | |
static int | play_file (struct ast_channel *chan, const char *filename) |
static int | pqm_exec (struct ast_channel *chan, void *data) |
PauseQueueMember application. | |
static int | ql_exec (struct ast_channel *chan, void *data) |
QueueLog application. | |
static int | queue_cmp_cb (void *obj, void *arg, int flags) |
static int | queue_exec (struct ast_channel *chan, void *data) |
The starting point for all queue calls. | |
static int | queue_function_memberpenalty_read (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_MEMBER_PENALTY() Gets the members penalty. | |
static int | queue_function_memberpenalty_write (struct ast_channel *chan, const char *cmd, char *data, const char *value) |
Dialplan function QUEUE_MEMBER_PENALTY() Sets the members penalty. | |
static int | queue_function_qac (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Get number either busy / free or total members of a specific queue. | |
static int | queue_function_qac_dep (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Get the total number of members in a specific queue (Deprecated). | |
static int | queue_function_queuememberlist (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_MEMBER_LIST() Get list of members in a specific queue. | |
static int | queue_function_queuewaitingcount (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
Dialplan function QUEUE_WAITING_COUNT() Get number callers waiting in a specific queue. | |
static int | queue_function_var (struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) |
create interface var with all queue details. | |
static int | queue_hash_cb (const void *obj, const int flags) |
static struct call_queue * | queue_ref (struct call_queue *q) |
static void | queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown) |
Configure a queue parameter. | |
static char * | queue_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
static void | queue_transfer_destroy (void *data) |
static void | queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) |
Log an attended transfer when a queue caller channel is masqueraded. | |
static struct call_queue * | queue_unref (struct call_queue *q) |
static void | recalc_holdtime (struct queue_ent *qe, int newholdtime) |
static void | record_abandoned (struct queue_ent *qe) |
Record that a caller gave up on waiting in queue. | |
static int | reload (void) |
static void | reload_queue_members (void) |
Reload dynamic queue members persisted into the astdb. | |
static int | reload_queue_rules (int reload) |
static int | reload_queues (int reload) |
static int | remove_from_interfaces (const char *interface, int lock_queue_container) |
static int | remove_from_queue (const char *queuename, const char *interface) |
Remove member from queue. | |
static int | ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies) |
Part 2 of ring_one. | |
static int | ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies) |
Place a call to a queue member. | |
static void | rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause) |
RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. | |
static int | rqm_exec (struct ast_channel *chan, void *data) |
RemoveQueueMember application. | |
static void | rt_handle_member_record (struct call_queue *q, char *interface, const char *rt_uniqueid, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface) |
Find rt member record to update otherwise create one. | |
static int | say_periodic_announcement (struct queue_ent *qe, int ringing) |
Playback announcement to queued members if peroid has elapsed. | |
static int | say_position (struct queue_ent *qe, int ringing) |
static void | send_agent_complete (const struct queue_ent *qe, const char *queuename, const struct ast_channel *peer, const struct member *member, time_t callstart, char *vars, size_t vars_len, enum agent_complete_reason rsn) |
Send out AMI message with member call completion status information. | |
static int | set_member_paused (const char *queuename, const char *interface, const char *reason, int paused) |
static int | set_member_penalty (char *queuename, char *interface, int penalty) |
static void | set_queue_result (struct ast_channel *chan, enum queue_result res) |
sets the QUEUESTATUS channel variable | |
static void | set_queue_variables (struct call_queue *q, struct ast_channel *chan) |
Set variables of queue. | |
static struct ast_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) |
update the queue status | |
static int | update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value) |
static void | update_realtime_members (struct call_queue *q) |
static int | update_status (const char *interface, const int status) |
set a member's status based on device state of that member's state_interface. | |
static int | upqm_exec (struct ast_channel *chan, void *data) |
UnPauseQueueMember application. | |
static int | valid_exit (struct queue_ent *qe, char digit) |
Check for valid exit from queue via goto. | |
static char * | vars2manager (struct ast_channel *chan, char *vars, size_t len) |
convert "\n" to "\nVariable: " ready for manager to use | |
static int | wait_a_bit (struct queue_ent *qe) |
static struct callattempt * | wait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed) |
Wait for a member to answer the call. | |
static int | wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason) |
The waiting areas for callers who are not actively calling members. | |
Variables | |
static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } |
static char * | app = "Queue" |
static char * | app_aqm = "AddQueueMember" |
static char * | app_aqm_descrip |
static char * | app_aqm_synopsis = "Dynamically adds queue members" |
static char * | app_pqm = "PauseQueueMember" |
static char * | app_pqm_descrip |
static char * | app_pqm_synopsis = "Pauses a queue member" |
static char * | app_ql = "QueueLog" |
static char * | app_ql_descrip |
static char * | app_ql_synopsis = "Writes to the queue_log" |
static char * | app_rqm = "RemoveQueueMember" |
static char * | app_rqm_descrip |
static char * | app_rqm_synopsis = "Dynamically removes queue members" |
static char * | app_upqm = "UnpauseQueueMember" |
static char * | app_upqm_descrip |
static char * | app_upqm_synopsis = "Unpauses a queue member" |
static struct ast_module_info * | ast_module_info = &__mod_info |
static int | autofill_default = 0 |
queues.conf [general] option | |
static struct ast_cli_entry | cli_queue [] |
static char * | descrip |
static struct ast_event_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 * | pm_family = "Queue/PersistentMembers" |
Persistent Members astdb family. | |
static const char | qpm_cmd_usage [] |
static const char | qsmp_cmd_usage [] |
static int | queue_keep_stats = 0 |
queues.conf [general] option | |
static int | queue_persistent_members = 0 |
queues.conf [general] option | |
struct { | |
enum queue_result id | |
char * text | |
} | queue_results [] |
static struct ast_datastore_info | queue_transfer_info |
a datastore used to help correctly log attended transfers of queue callers | |
static struct ast_custom_function | queuemembercount_dep |
static struct ast_custom_function | queuemembercount_function |
static struct ast_custom_function | queuememberlist_function |
static struct ast_custom_function | queuememberpenalty_function |
static struct ao2_container * | queues |
static struct ast_custom_function | queuevar_function |
static struct ast_custom_function | queuewaitingcount_function |
static const char | qum_cmd_usage [] |
static int | shared_lastcall = 0 |
queues.conf [general] option | |
static struct strategy | strategies [] |
static char * | synopsis = "Queue a call for a call queue" |
static int | update_cdr = 0 |
queues.conf [general] option | |
static int | use_weight = 0 |
queues.conf per-queue weight option |
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 434 of file app_queue.c.
Referenced by queue_set_param(), and say_position().
#define ANNOUNCEPOSITION_MORE_THAN 3 |
We say "Currently there are more than <limit>"
Definition at line 433 of file app_queue.c.
Referenced by queue_set_param(), and say_position().
#define ANNOUNCEPOSITION_NO 2 |
We don't announce position
Definition at line 432 of file app_queue.c.
Referenced by queue_set_param().
#define ANNOUNCEPOSITION_YES 1 |
We announce position
Definition at line 431 of file app_queue.c.
Referenced by init_queue(), queue_set_param(), and say_position().
#define AST_MAX_WATCHERS 256 |
Definition at line 2563 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 142 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 141 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 280 of file app_queue.c.
Referenced by dump_queue_members(), and reload_queue_members().
#define QUEUE_EMPTY_LOOSE 3 |
Definition at line 417 of file app_queue.c.
Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().
#define QUEUE_EMPTY_NORMAL 1 |
#define QUEUE_EMPTY_STRICT 2 |
Definition at line 416 of file app_queue.c.
Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().
#define QUEUE_EVENT_VARIABLES 3 |
Definition at line 420 of file app_queue.c.
Referenced by queue_set_param(), ring_entry(), send_agent_complete(), and try_calling().
#define RECHECK 1 |
Recheck every second to see we we're at the top yet
Definition at line 140 of file app_queue.c.
Referenced by wait_our_turn().
#define RES_EXISTS (-1) |
Entry already exists
Definition at line 147 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 149 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 150 of file app_queue.c.
Referenced by handle_queue_add_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_OKAY 0 |
Action completed
Definition at line 146 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 148 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 112 of file app_queue.c.
00112 { 00113 QUEUE_STRATEGY_RINGALL = 0, 00114 QUEUE_STRATEGY_LEASTRECENT, 00115 QUEUE_STRATEGY_FEWESTCALLS, 00116 QUEUE_STRATEGY_RANDOM, 00117 QUEUE_STRATEGY_RRMEMORY, 00118 QUEUE_STRATEGY_LINEAR, 00119 QUEUE_STRATEGY_WRANDOM 00120 };
enum queue_member_status |
QUEUE_NO_MEMBERS | |
QUEUE_NO_REACHABLE_MEMBERS | |
QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS | |
QUEUE_NORMAL |
Definition at line 654 of file app_queue.c.
00654 { 00655 QUEUE_NO_MEMBERS, 00656 QUEUE_NO_REACHABLE_MEMBERS, 00657 QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, 00658 QUEUE_NORMAL 00659 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL | |
QUEUE_CONTINUE |
Definition at line 306 of file app_queue.c.
00306 { 00307 QUEUE_UNKNOWN = 0, 00308 QUEUE_TIMEOUT = 1, 00309 QUEUE_JOINEMPTY = 2, 00310 QUEUE_LEAVEEMPTY = 3, 00311 QUEUE_JOINUNAVAIL = 4, 00312 QUEUE_LEAVEUNAVAIL = 5, 00313 QUEUE_FULL = 6, 00314 QUEUE_CONTINUE = 7, 00315 };
Definition at line 331 of file app_queue.c.
00331 { 00332 TIMEOUT_PRIORITY_APP, 00333 TIMEOUT_PRIORITY_CONF, 00334 };
static char* __queues_show | ( | struct mansession * | s, | |
int | fd, | |||
int | argc, | |||
char ** | argv | |||
) | [static] |
Show queue(s) status and statistics.
List the queues strategy, calls processed, members logged in, other queue statistics such as avg hold time.
Definition at line 5679 of file app_queue.c.
References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_load_realtime_multientry(), ast_str_alloca, ast_str_append(), ast_str_set(), ast_strlen_zero(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, CLI_SHOWUSAGE, CLI_SUCCESS, call_queue::count, devstate2str(), do_print(), member::dynamic, F_AO2I_DONTLOCK, 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_unref(), queues, member::realtime, call_queue::realtime, s, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, ast_str::str, call_queue::strategy, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
05680 { 05681 struct call_queue *q; 05682 struct ast_str *out = ast_str_alloca(240); 05683 int found = 0; 05684 time_t now = time(NULL); 05685 struct ao2_iterator queue_iter; 05686 struct ao2_iterator mem_iter; 05687 05688 if (argc != 2 && argc != 3) 05689 return CLI_SHOWUSAGE; 05690 05691 if (argc == 3) { /* specific queue */ 05692 if ((q = load_realtime_queue(argv[2]))) { 05693 queue_unref(q); 05694 } 05695 } else if (ast_check_realtime("queues")) { 05696 /* This block is to find any queues which are defined in realtime but 05697 * which have not yet been added to the in-core container 05698 */ 05699 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 05700 char *queuename; 05701 if (cfg) { 05702 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 05703 if ((q = load_realtime_queue(queuename))) { 05704 queue_unref(q); 05705 } 05706 } 05707 ast_config_destroy(cfg); 05708 } 05709 } 05710 05711 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK); 05712 ao2_lock(queues); 05713 while ((q = ao2_iterator_next(&queue_iter))) { 05714 float sl; 05715 struct call_queue *realtime_queue = NULL; 05716 05717 ao2_lock(q); 05718 /* This check is to make sure we don't print information for realtime 05719 * queues which have been deleted from realtime but which have not yet 05720 * been deleted from the in-core container 05721 */ 05722 if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) { 05723 ao2_unlock(q); 05724 queue_unref(q); 05725 continue; 05726 } else if (q->realtime) { 05727 queue_unref(realtime_queue); 05728 } 05729 if (argc == 3 && strcasecmp(q->name, argv[2])) { 05730 ao2_unlock(q); 05731 queue_unref(q); 05732 continue; 05733 } 05734 found = 1; 05735 05736 ast_str_set(&out, 0, "%-12.12s has %d calls (max ", q->name, q->count); 05737 if (q->maxlen) 05738 ast_str_append(&out, 0, "%d", q->maxlen); 05739 else 05740 ast_str_append(&out, 0, "unlimited"); 05741 sl = 0; 05742 if (q->callscompleted > 0) 05743 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 05744 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds", 05745 int2strat(q->strategy), q->holdtime, q->weight, 05746 q->callscompleted, q->callsabandoned,sl,q->servicelevel); 05747 do_print(s, fd, out->str); 05748 if (!ao2_container_count(q->members)) 05749 do_print(s, fd, " No Members"); 05750 else { 05751 struct member *mem; 05752 05753 do_print(s, fd, " Members: "); 05754 mem_iter = ao2_iterator_init(q->members, 0); 05755 while ((mem = ao2_iterator_next(&mem_iter))) { 05756 ast_str_set(&out, 0, " %s", mem->membername); 05757 if (strcasecmp(mem->membername, mem->interface)) { 05758 ast_str_append(&out, 0, " (%s)", mem->interface); 05759 } 05760 if (mem->penalty) 05761 ast_str_append(&out, 0, " with penalty %d", mem->penalty); 05762 ast_str_append(&out, 0, "%s%s%s (%s)", 05763 mem->dynamic ? " (dynamic)" : "", 05764 mem->realtime ? " (realtime)" : "", 05765 mem->paused ? " (paused)" : "", 05766 devstate2str(mem->status)); 05767 if (mem->calls) 05768 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)", 05769 mem->calls, (long) (time(NULL) - mem->lastcall)); 05770 else 05771 ast_str_append(&out, 0, " has taken no calls yet"); 05772 do_print(s, fd, out->str); 05773 ao2_ref(mem, -1); 05774 } 05775 } 05776 if (!q->head) 05777 do_print(s, fd, " No Callers"); 05778 else { 05779 struct queue_ent *qe; 05780 int pos = 1; 05781 05782 do_print(s, fd, " Callers: "); 05783 for (qe = q->head; qe; qe = qe->next) { 05784 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)", 05785 pos++, qe->chan->name, (long) (now - qe->start) / 60, 05786 (long) (now - qe->start) % 60, qe->prio); 05787 do_print(s, fd, out->str); 05788 } 05789 } 05790 do_print(s, fd, ""); /* blank line between entries */ 05791 ao2_unlock(q); 05792 queue_unref(q); /* Unref the iterator's reference */ 05793 } 05794 ao2_unlock(queues); 05795 if (!found) { 05796 if (argc == 3) 05797 ast_str_set(&out, 0, "No such queue: %s.", argv[2]); 05798 else 05799 ast_str_set(&out, 0, "No queues."); 05800 do_print(s, fd, out->str); 05801 } 05802 return CLI_SUCCESS; 05803 }
static void __reg_module | ( | void | ) | [static] |
Definition at line 6737 of file app_queue.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 6737 of file app_queue.c.
static int add_to_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 962 of file app_queue.c.
References ast_calloc, ast_copy_string(), ast_debug, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member_interface::interface, and member_interface::list.
Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().
00963 { 00964 struct member_interface *curint; 00965 00966 AST_LIST_LOCK(&interfaces); 00967 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00968 if (!strcasecmp(curint->interface, interface)) 00969 break; 00970 } 00971 00972 if (curint) { 00973 AST_LIST_UNLOCK(&interfaces); 00974 return 0; 00975 } 00976 00977 ast_debug(1, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface); 00978 00979 if ((curint = ast_calloc(1, sizeof(*curint)))) { 00980 ast_copy_string(curint->interface, interface, sizeof(curint->interface)); 00981 AST_LIST_INSERT_HEAD(&interfaces, curint, list); 00982 } 00983 AST_LIST_UNLOCK(&interfaces); 00984 00985 return 0; 00986 }
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 4157 of file app_queue.c.
References add_to_interfaces(), ao2_link, ao2_lock(), ao2_ref, ao2_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), manager_event, call_queue::membercount, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, member::state_interface, and member::status.
Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().
04158 { 04159 struct call_queue *q; 04160 struct member *new_member, *old_member; 04161 int res = RES_NOSUCHQUEUE; 04162 04163 /*! \note Ensure the appropriate realtime queue is loaded. Note that this 04164 * short-circuits if the queue is already in memory. */ 04165 if (!(q = load_realtime_queue(queuename))) 04166 return res; 04167 04168 ao2_lock(queues); 04169 04170 ao2_lock(q); 04171 if ((old_member = interface_exists(q, interface)) == NULL) { 04172 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) { 04173 add_to_interfaces(new_member->state_interface); 04174 new_member->dynamic = 1; 04175 ao2_link(q->members, new_member); 04176 q->membercount++; 04177 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 04178 "Queue: %s\r\n" 04179 "Location: %s\r\n" 04180 "MemberName: %s\r\n" 04181 "Membership: %s\r\n" 04182 "Penalty: %d\r\n" 04183 "CallsTaken: %d\r\n" 04184 "LastCall: %d\r\n" 04185 "Status: %d\r\n" 04186 "Paused: %d\r\n", 04187 q->name, new_member->interface, new_member->membername, 04188 "dynamic", 04189 new_member->penalty, new_member->calls, (int) new_member->lastcall, 04190 new_member->status, new_member->paused); 04191 04192 ao2_ref(new_member, -1); 04193 new_member = NULL; 04194 04195 if (dump) 04196 dump_queue_members(q); 04197 04198 res = RES_OKAY; 04199 } else { 04200 res = RES_OUTOFMEMORY; 04201 } 04202 } else { 04203 ao2_ref(old_member, -1); 04204 res = RES_EXISTS; 04205 } 04206 ao2_unlock(q); 04207 ao2_unlock(queues); 04208 04209 return res; 04210 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static] |
Definition at line 1436 of file app_queue.c.
References ao2_alloc, ao2_ref, ast_string_field_init, ast_string_field_set, and destroy_queue().
Referenced by find_queue_by_name_rt(), and reload_queues().
01437 { 01438 struct call_queue *q; 01439 01440 if ((q = ao2_alloc(sizeof(*q), destroy_queue))) { 01441 if (ast_string_field_init(q, 64)) { 01442 ao2_ref(q, -1); 01443 return NULL; 01444 } 01445 ast_string_field_set(q, name, queuename); 01446 } 01447 return q; 01448 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
AddQueueMember application.
Definition at line 4591 of file app_queue.c.
References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, 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().
04592 { 04593 int res=-1; 04594 char *parse, *temppos = NULL; 04595 AST_DECLARE_APP_ARGS(args, 04596 AST_APP_ARG(queuename); 04597 AST_APP_ARG(interface); 04598 AST_APP_ARG(penalty); 04599 AST_APP_ARG(options); 04600 AST_APP_ARG(membername); 04601 AST_APP_ARG(state_interface); 04602 ); 04603 int penalty = 0; 04604 04605 if (ast_strlen_zero(data)) { 04606 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n"); 04607 return -1; 04608 } 04609 04610 parse = ast_strdupa(data); 04611 04612 AST_STANDARD_APP_ARGS(args, parse); 04613 04614 if (ast_strlen_zero(args.interface)) { 04615 args.interface = ast_strdupa(chan->name); 04616 temppos = strrchr(args.interface, '-'); 04617 if (temppos) 04618 *temppos = '\0'; 04619 } 04620 04621 if (!ast_strlen_zero(args.penalty)) { 04622 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) { 04623 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 04624 penalty = 0; 04625 } 04626 } 04627 04628 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) { 04629 case RES_OKAY: 04630 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 04631 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 04632 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 04633 res = 0; 04634 break; 04635 case RES_EXISTS: 04636 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 04637 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 04638 res = 0; 04639 break; 04640 case RES_NOSUCHQUEUE: 04641 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 04642 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 04643 res = 0; 04644 break; 04645 case RES_OUTOFMEMORY: 04646 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 04647 break; 04648 } 04649 04650 return res; 04651 }
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 3218 of file app_queue.c.
References ast_channel_datastore_find(), chan, and queue_transfer_info.
Referenced by try_calling().
03219 { 03220 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 03221 }
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 3057 of file app_queue.c.
References ast_log(), ast_random(), member::calls, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, queue_ent::min_penalty, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_RRMEMORY, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
Referenced by try_calling().
03058 { 03059 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty))) 03060 return -1; 03061 03062 switch (q->strategy) { 03063 case QUEUE_STRATEGY_RINGALL: 03064 /* Everyone equal, except for penalty */ 03065 tmp->metric = mem->penalty * 1000000; 03066 break; 03067 case QUEUE_STRATEGY_LINEAR: 03068 if (pos < qe->linpos) { 03069 tmp->metric = 1000 + pos; 03070 } else { 03071 if (pos > qe->linpos) 03072 /* Indicate there is another priority */ 03073 qe->linwrapped = 1; 03074 tmp->metric = pos; 03075 } 03076 tmp->metric += mem->penalty * 1000000; 03077 break; 03078 case QUEUE_STRATEGY_RRMEMORY: 03079 if (pos < q->rrpos) { 03080 tmp->metric = 1000 + pos; 03081 } else { 03082 if (pos > q->rrpos) 03083 /* Indicate there is another priority */ 03084 q->wrapped = 1; 03085 tmp->metric = pos; 03086 } 03087 tmp->metric += mem->penalty * 1000000; 03088 break; 03089 case QUEUE_STRATEGY_RANDOM: 03090 tmp->metric = ast_random() % 1000; 03091 tmp->metric += mem->penalty * 1000000; 03092 break; 03093 case QUEUE_STRATEGY_WRANDOM: 03094 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000); 03095 break; 03096 case QUEUE_STRATEGY_FEWESTCALLS: 03097 tmp->metric = mem->calls; 03098 tmp->metric += mem->penalty * 1000000; 03099 break; 03100 case QUEUE_STRATEGY_LEASTRECENT: 03101 if (!mem->lastcall) 03102 tmp->metric = 0; 03103 else 03104 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 03105 tmp->metric += mem->penalty * 1000000; 03106 break; 03107 default: 03108 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 03109 break; 03110 } 03111 return 0; 03112 }
static void clear_and_free_interfaces | ( | void | ) | [static] |
Definition at line 1036 of file app_queue.c.
References ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and member_interface::list.
Referenced by unload_module().
01037 { 01038 struct member_interface *curint; 01039 01040 AST_LIST_LOCK(&interfaces); 01041 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list))) 01042 ast_free(curint); 01043 AST_LIST_UNLOCK(&interfaces); 01044 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 953 of file app_queue.c.
References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_queues().
00954 { 00955 q->holdtime = 0; 00956 q->callscompleted = 0; 00957 q->callsabandoned = 0; 00958 q->callscompletedinsl = 0; 00959 q->wrapuptime = 0; 00960 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 2123 of file app_queue.c.
References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, call_queue::count, member::interface, call_queue::members, call_queue::name, num_available_members(), OBJ_POINTER, queue_unref(), queues, and call_queue::weight.
Referenced by ring_entry().
02124 { 02125 struct call_queue *q; 02126 struct member *mem; 02127 int found = 0; 02128 struct ao2_iterator queue_iter; 02129 02130 /* q's lock and rq's lock already set by try_calling() 02131 * to solve deadlock */ 02132 queue_iter = ao2_iterator_init(queues, 0); 02133 while ((q = ao2_iterator_next(&queue_iter))) { 02134 if (q == rq) { /* don't check myself, could deadlock */ 02135 queue_unref(q); 02136 continue; 02137 } 02138 ao2_lock(q); 02139 if (q->count && q->members) { 02140 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 02141 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 02142 if (q->weight > rq->weight && q->count >= num_available_members(q)) { 02143 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); 02144 found = 1; 02145 } 02146 ao2_ref(mem, -1); 02147 } 02148 } 02149 ao2_unlock(q); 02150 queue_unref(q); 02151 if (found) { 02152 break; 02153 } 02154 } 02155 return found; 02156 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5805 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ast_strdup, call_queue::name, queue_unref(), and queues.
Referenced by complete_queue_add_member(), complete_queue_pause_member(), complete_queue_remove_member(), complete_queue_set_member_penalty(), and complete_queue_show().
05806 { 05807 struct call_queue *q; 05808 char *ret = NULL; 05809 int which = 0; 05810 int wordlen = strlen(word); 05811 struct ao2_iterator queue_iter; 05812 05813 queue_iter = ao2_iterator_init(queues, 0); 05814 while ((q = ao2_iterator_next(&queue_iter))) { 05815 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 05816 ret = ast_strdup(q->name); 05817 queue_unref(q); 05818 break; 05819 } 05820 queue_unref(q); 05821 } 05822 05823 return ret; 05824 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6182 of file app_queue.c.
References ast_malloc, ast_strdup, complete_queue(), and num.
Referenced by handle_queue_add_member().
06183 { 06184 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 06185 switch (pos) { 06186 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 06187 return NULL; 06188 case 4: /* only one possible match, "to" */ 06189 return state == 0 ? ast_strdup("to") : NULL; 06190 case 5: /* <queue> */ 06191 return complete_queue(line, word, pos, state); 06192 case 6: /* only one possible match, "penalty" */ 06193 return state == 0 ? ast_strdup("penalty") : NULL; 06194 case 7: 06195 if (state < 100) { /* 0-99 */ 06196 char *num; 06197 if ((num = ast_malloc(3))) { 06198 sprintf(num, "%d", state); 06199 } 06200 return num; 06201 } else { 06202 return NULL; 06203 } 06204 case 8: /* only one possible match, "as" */ 06205 return state == 0 ? ast_strdup("as") : NULL; 06206 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 06207 return NULL; 06208 default: 06209 return NULL; 06210 } 06211 }
static char* complete_queue_pause_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6396 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_pause_member().
06397 { 06398 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */ 06399 switch (pos) { 06400 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 06401 return NULL; 06402 case 4: /* only one possible match, "queue" */ 06403 return state == 0 ? ast_strdup("queue") : NULL; 06404 case 5: /* <queue> */ 06405 return complete_queue(line, word, pos, state); 06406 case 6: /* "reason" */ 06407 return state == 0 ? ast_strdup("reason") : NULL; 06408 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */ 06409 return NULL; 06410 default: 06411 return NULL; 06412 } 06413 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6312 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_strdup, complete_queue(), member::interface, member::membername, queue_unref(), and queues.
Referenced by handle_queue_remove_member().
06313 { 06314 int which = 0; 06315 struct call_queue *q; 06316 struct member *m; 06317 struct ao2_iterator queue_iter; 06318 struct ao2_iterator mem_iter; 06319 int wordlen = strlen(word); 06320 06321 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 06322 if (pos > 5 || pos < 3) 06323 return NULL; 06324 if (pos == 4) /* only one possible match, 'from' */ 06325 return (state == 0 ? ast_strdup("from") : NULL); 06326 06327 if (pos == 5) /* No need to duplicate code */ 06328 return complete_queue(line, word, pos, state); 06329 06330 /* here is the case for 3, <member> */ 06331 queue_iter = ao2_iterator_init(queues, 0); 06332 while ((q = ao2_iterator_next(&queue_iter))) { 06333 ao2_lock(q); 06334 mem_iter = ao2_iterator_init(q->members, 0); 06335 while ((m = ao2_iterator_next(&mem_iter))) { 06336 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) { 06337 char *tmp; 06338 ao2_unlock(q); 06339 tmp = ast_strdup(m->interface); 06340 ao2_ref(m, -1); 06341 queue_unref(q); 06342 return tmp; 06343 } 06344 ao2_ref(m, -1); 06345 } 06346 ao2_unlock(q); 06347 queue_unref(q); 06348 } 06349 06350 return NULL; 06351 }
static char* complete_queue_rule_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6529 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().
06530 { 06531 int which = 0; 06532 struct rule_list *rl_iter; 06533 int wordlen = strlen(word); 06534 char *ret = NULL; 06535 if (pos != 3) /* Wha? */ { 06536 return NULL; 06537 } 06538 06539 AST_LIST_LOCK(&rule_lists); 06540 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 06541 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) { 06542 ret = ast_strdup(rl_iter->name); 06543 break; 06544 } 06545 } 06546 AST_LIST_UNLOCK(&rule_lists); 06547 06548 return ret; 06549 }
static char* complete_queue_set_member_penalty | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 6466 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_set_member_penalty().
06467 { 06468 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/ 06469 switch (pos) { 06470 case 4: 06471 if (state == 0) { 06472 return ast_strdup("on"); 06473 } else { 06474 return NULL; 06475 } 06476 case 6: 06477 if (state == 0) { 06478 return ast_strdup("in"); 06479 } else { 06480 return NULL; 06481 } 06482 case 7: 06483 return complete_queue(line, word, pos, state); 06484 default: 06485 return NULL; 06486 } 06487 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5826 of file app_queue.c.
References complete_queue().
Referenced by queue_show().
05827 { 05828 if (pos == 2) 05829 return complete_queue(line, word, pos, state); 05830 return NULL; 05831 }
static int compress_char | ( | const char | c | ) | [static] |
Definition at line 851 of file app_queue.c.
00852 { 00853 if (c < 32) 00854 return 0; 00855 else if (c > 96) 00856 return c - 64; 00857 else 00858 return c - 32; 00859 }
static void copy_rules | ( | struct queue_ent * | qe, | |
const char * | rulename | |||
) | [static] |
Copy rule from global list into specified queue.
Definition at line 4688 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(), 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::qe_rules, rule_list::rules, and penalty_rule::time.
Referenced by join_queue().
04689 { 04690 struct penalty_rule *pr_iter; 04691 struct rule_list *rl_iter; 04692 const char *tmp = rulename; 04693 if (ast_strlen_zero(tmp)) { 04694 return; 04695 } 04696 AST_LIST_LOCK(&rule_lists); 04697 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 04698 if (!strcasecmp(rl_iter->name, tmp)) 04699 break; 04700 } 04701 if (rl_iter) { 04702 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 04703 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr)); 04704 if (!new_pr) { 04705 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n"); 04706 AST_LIST_UNLOCK(&rule_lists); 04707 break; 04708 } 04709 new_pr->time = pr_iter->time; 04710 new_pr->max_value = pr_iter->max_value; 04711 new_pr->min_value = pr_iter->min_value; 04712 new_pr->max_relative = pr_iter->max_relative; 04713 new_pr->min_relative = pr_iter->min_relative; 04714 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list); 04715 } 04716 } 04717 AST_LIST_UNLOCK(&rule_lists); 04718 }
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 826 of file app_queue.c.
References ao2_alloc, ast_copy_string(), ast_device_state(), ast_log(), ast_strlen_zero(), LOG_WARNING, and member::penalty.
Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().
00827 { 00828 struct member *cur; 00829 00830 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 00831 cur->penalty = penalty; 00832 cur->paused = paused; 00833 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 00834 if (!ast_strlen_zero(state_interface)) 00835 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); 00836 else 00837 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface)); 00838 if (!ast_strlen_zero(membername)) 00839 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 00840 else 00841 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 00842 if (!strchr(cur->interface, '/')) 00843 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 00844 cur->status = ast_device_state(cur->state_interface); 00845 } 00846 00847 return cur; 00848 }
static void destroy_queue | ( | void * | obj | ) | [static] |
Free queue's member list then its string fields.
Definition at line 1422 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().
01423 { 01424 struct call_queue *q = obj; 01425 int i; 01426 01427 free_members(q, 1); 01428 ast_string_field_free_memory(q); 01429 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 01430 if (q->sound_periodicannounce[i]) 01431 free(q->sound_periodicannounce[i]); 01432 } 01433 ao2_ref(q->members, -1); 01434 }
static void device_state_cb | ( | const struct ast_event * | event, | |
void * | unused | |||
) | [static] |
Definition at line 799 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.
00800 { 00801 enum ast_device_state state; 00802 const char *device; 00803 struct statechange *sc; 00804 size_t datapsize; 00805 00806 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); 00807 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); 00808 00809 if (ast_strlen_zero(device)) { 00810 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n"); 00811 return; 00812 } 00813 datapsize = sizeof(*sc) + strlen(device) + 1; 00814 if (!(sc = ast_calloc(1, datapsize))) { 00815 ast_log(LOG_ERROR, "failed to calloc a state change struct\n"); 00816 return; 00817 } 00818 sc->state = state; 00819 strcpy(sc->dev, device); 00820 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) { 00821 ast_free(sc); 00822 } 00823 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 2159 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry().
02160 { 02161 o->stillgoing = 0; 02162 ast_hangup(o->chan); 02163 o->chan = NULL; 02164 }
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 5665 of file app_queue.c.
References ast_cli(), astman_append(), and s.
Referenced by __queues_show().
05666 { 05667 if (s) 05668 astman_append(s, "%s\r\n", str); 05669 else 05670 ast_cli(fd, "%s\n", str); 05671 }
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 4058 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, PM_MAX_LEN, and member::state_interface.
Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().
04059 { 04060 struct member *cur_member; 04061 char value[PM_MAX_LEN]; 04062 int value_len = 0; 04063 int res; 04064 struct ao2_iterator mem_iter; 04065 04066 memset(value, 0, sizeof(value)); 04067 04068 if (!pm_queue) 04069 return; 04070 04071 mem_iter = ao2_iterator_init(pm_queue->members, 0); 04072 while ((cur_member = ao2_iterator_next(&mem_iter))) { 04073 if (!cur_member->dynamic) { 04074 ao2_ref(cur_member, -1); 04075 continue; 04076 } 04077 04078 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s", 04079 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface); 04080 04081 ao2_ref(cur_member, -1); 04082 04083 if (res != strlen(value + value_len)) { 04084 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 04085 break; 04086 } 04087 value_len += res; 04088 } 04089 04090 if (value_len && !cur_member) { 04091 if (ast_db_put(pm_family, pm_queue->name, value)) 04092 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 04093 } else 04094 /* Delete the entry if the queue is empty or there is an error */ 04095 ast_db_del(pm_family, pm_queue->name); 04096 }
static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 3265 of file app_queue.c.
References ao2_lock(), ao2_ref, ao2_unlock(), queue_end_bridge::chan, chan, queue_end_bridge::q, queue_unref(), and set_queue_variables().
03266 { 03267 struct queue_end_bridge *qeb = data; 03268 struct call_queue *q = qeb->q; 03269 struct ast_channel *chan = qeb->chan; 03270 03271 if (ao2_ref(qeb, -1) == 1) { 03272 ao2_lock(q); 03273 set_queue_variables(q, chan); 03274 ao2_unlock(q); 03275 /* This unrefs the reference we made in try_calling when we allocated qeb */ 03276 queue_unref(q); 03277 } 03278 }
static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, | |
struct ast_channel * | originator, | |||
struct ast_channel * | terminator | |||
) | [static] |
Definition at line 3258 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, and ast_bridge_config::end_bridge_callback_data.
03259 { 03260 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data; 03261 ao2_ref(qeb, +1); 03262 qeb->chan = originator; 03263 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static] |
find the entry with the best metric, or NULL
Definition at line 2355 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
02356 { 02357 struct callattempt *best = NULL, *cur; 02358 02359 for (cur = outgoing; cur; cur = cur->q_next) { 02360 if (cur->stillgoing && /* Not already done */ 02361 !cur->chan && /* Isn't already going */ 02362 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 02363 best = cur; 02364 } 02365 } 02366 02367 return best; 02368 }
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 1460 of file app_queue.c.
References alloc_queue(), ao2_find, ao2_lock(), ao2_unlink, ao2_unlock(), ast_debug, ast_log(), clear_queue(), LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, OBJ_POINTER, QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, strat2int(), call_queue::strategy, and ast_variable::value.
Referenced by load_realtime_queue().
01461 { 01462 struct ast_variable *v; 01463 struct call_queue *q, tmpq = { 01464 .name = queuename, 01465 }; 01466 struct member *m; 01467 struct ao2_iterator mem_iter; 01468 char *interface = NULL; 01469 const char *tmp_name; 01470 char *tmp; 01471 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 01472 01473 /* Static queues override realtime. */ 01474 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 01475 ao2_lock(q); 01476 if (!q->realtime) { 01477 if (q->dead) { 01478 ao2_unlock(q); 01479 queue_unref(q); 01480 return NULL; 01481 } else { 01482 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 01483 ao2_unlock(q); 01484 return q; 01485 } 01486 } 01487 queue_unref(q); 01488 } else if (!member_config) 01489 /* Not found in the list, and it's not realtime ... */ 01490 return NULL; 01491 01492 /* Check if queue is defined in realtime. */ 01493 if (!queue_vars) { 01494 /* Delete queue from in-core list if it has been deleted in realtime. */ 01495 if (q) { 01496 /*! \note Hmm, can't seem to distinguish a DB failure from a not 01497 found condition... So we might delete an in-core queue 01498 in case of DB failure. */ 01499 ast_debug(1, "Queue %s not found in realtime.\n", queuename); 01500 01501 q->dead = 1; 01502 /* Delete if unused (else will be deleted when last caller leaves). */ 01503 ao2_unlink(queues, q); 01504 ao2_unlock(q); 01505 queue_unref(q); 01506 } 01507 return NULL; 01508 } 01509 01510 /* Create a new queue if an in-core entry does not exist yet. */ 01511 if (!q) { 01512 struct ast_variable *tmpvar = NULL; 01513 if (!(q = alloc_queue(queuename))) 01514 return NULL; 01515 ao2_lock(q); 01516 clear_queue(q); 01517 q->realtime = 1; 01518 /*Before we initialize the queue, we need to set the strategy, so that linear strategy 01519 * will allocate the members properly 01520 */ 01521 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { 01522 if (!strcasecmp(tmpvar->name, "strategy")) { 01523 q->strategy = strat2int(tmpvar->value); 01524 if (q->strategy < 0) { 01525 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01526 tmpvar->value, q->name); 01527 q->strategy = QUEUE_STRATEGY_RINGALL; 01528 } 01529 break; 01530 } 01531 } 01532 /* We traversed all variables and didn't find a strategy */ 01533 if (!tmpvar) 01534 q->strategy = QUEUE_STRATEGY_RINGALL; 01535 ao2_link(queues, q); 01536 } 01537 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 01538 01539 memset(tmpbuf, 0, sizeof(tmpbuf)); 01540 for (v = queue_vars; v; v = v->next) { 01541 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 01542 if ((tmp = strchr(v->name, '_'))) { 01543 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 01544 tmp_name = tmpbuf; 01545 tmp = tmpbuf; 01546 while ((tmp = strchr(tmp, '_'))) 01547 *tmp++ = '-'; 01548 } else 01549 tmp_name = v->name; 01550 01551 if (!ast_strlen_zero(v->value)) { 01552 /* Don't want to try to set the option if the value is empty */ 01553 queue_set_param(q, tmp_name, v->value, -1, 0); 01554 } 01555 } 01556 01557 /* Temporarily set realtime members dead so we can detect deleted ones. 01558 * Also set the membercount correctly for realtime*/ 01559 mem_iter = ao2_iterator_init(q->members, 0); 01560 while ((m = ao2_iterator_next(&mem_iter))) { 01561 q->membercount++; 01562 if (m->realtime) 01563 m->dead = 1; 01564 ao2_ref(m, -1); 01565 } 01566 01567 while ((interface = ast_category_browse(member_config, interface))) { 01568 rt_handle_member_record(q, interface, 01569 ast_variable_retrieve(member_config, interface, "uniqueid"), 01570 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface), 01571 ast_variable_retrieve(member_config, interface, "penalty"), 01572 ast_variable_retrieve(member_config, interface, "paused"), 01573 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface)); 01574 } 01575 01576 /* Delete all realtime members that have been deleted in DB. */ 01577 mem_iter = ao2_iterator_init(q->members, 0); 01578 while ((m = ao2_iterator_next(&mem_iter))) { 01579 if (m->dead) { 01580 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 01581 ao2_unlink(q->members, m); 01582 remove_from_interfaces(m->state_interface, 0); 01583 q->membercount--; 01584 } 01585 ao2_ref(m, -1); 01586 } 01587 01588 ao2_unlock(q); 01589 01590 return q; 01591 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Iterate through queue's member list and delete them.
Definition at line 1405 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, member::dynamic, call_queue::membercount, call_queue::members, remove_from_interfaces(), and member::state_interface.
Referenced by destroy_queue().
01406 { 01407 /* Free non-dynamic members */ 01408 struct member *cur; 01409 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01410 01411 while ((cur = ao2_iterator_next(&mem_iter))) { 01412 if (all || !cur->dynamic) { 01413 ao2_unlink(q->members, cur); 01414 remove_from_interfaces(cur->state_interface, 1); 01415 q->membercount--; 01416 } 01417 ao2_ref(cur, -1); 01418 } 01419 }
static int get_member_penalty | ( | char * | queuename, | |
char * | interface | |||
) | [static] |
Definition at line 4335 of file app_queue.c.
References ao2_find, ao2_lock(), ao2_ref, ao2_unlock(), ast_log(), interface_exists(), LOG_ERROR, OBJ_POINTER, member::penalty, queue_unref(), queues, and RESULT_FAILURE.
Referenced by queue_function_memberpenalty_read().
04336 { 04337 int foundqueue = 0, penalty; 04338 struct call_queue *q, tmpq = { 04339 .name = queuename, 04340 }; 04341 struct member *mem; 04342 04343 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 04344 foundqueue = 1; 04345 ao2_lock(q); 04346 if ((mem = interface_exists(q, interface))) { 04347 penalty = mem->penalty; 04348 ao2_ref(mem, -1); 04349 ao2_unlock(q); 04350 queue_unref(q); 04351 return penalty; 04352 } 04353 ao2_unlock(q); 04354 queue_unref(q); 04355 } 04356 04357 /* some useful debuging */ 04358 if (foundqueue) 04359 ast_log (LOG_ERROR, "Invalid queuename\n"); 04360 else 04361 ast_log (LOG_ERROR, "Invalid interface\n"); 04362 04363 return RESULT_FAILURE; 04364 }
static enum queue_member_status get_member_status | ( | struct call_queue * | q, | |
int | max_penalty, | |||
int | min_penalty | |||
) | [static] |
Check if members are available.
This function checks to see if members are available to be called. If any member is available, the function immediately returns QUEUE_NORMAL. If no members are available, the appropriate reason why is returned
Definition at line 667 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, and member::status.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
00668 { 00669 struct member *member; 00670 struct ao2_iterator mem_iter; 00671 enum queue_member_status result = QUEUE_NO_MEMBERS; 00672 00673 ao2_lock(q); 00674 mem_iter = ao2_iterator_init(q->members, 0); 00675 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) { 00676 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) 00677 continue; 00678 00679 switch (member->status) { 00680 case AST_DEVICE_INVALID: 00681 /* nothing to do */ 00682 break; 00683 case AST_DEVICE_UNAVAILABLE: 00684 if (result != QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS) 00685 result = QUEUE_NO_REACHABLE_MEMBERS; 00686 break; 00687 default: 00688 if (member->paused) { 00689 result = QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS; 00690 } else { 00691 ao2_unlock(q); 00692 ao2_ref(member, -1); 00693 return QUEUE_NORMAL; 00694 } 00695 break; 00696 } 00697 } 00698 00699 ao2_unlock(q); 00700 return result; 00701 }
static char* handle_queue_add_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6238 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.
06239 { 06240 char *queuename, *interface, *membername = NULL, *state_interface = NULL; 06241 int penalty; 06242 06243 switch ( cmd ) { 06244 case CLI_INIT: 06245 e->command = "queue add member"; 06246 e->usage = 06247 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n" 06248 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n"; 06249 return NULL; 06250 case CLI_GENERATE: 06251 return complete_queue_add_member(a->line, a->word, a->pos, a->n); 06252 } 06253 06254 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) { 06255 return CLI_SHOWUSAGE; 06256 } else if (strcmp(a->argv[4], "to")) { 06257 return CLI_SHOWUSAGE; 06258 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) { 06259 return CLI_SHOWUSAGE; 06260 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) { 06261 return CLI_SHOWUSAGE; 06262 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) { 06263 return CLI_SHOWUSAGE; 06264 } 06265 06266 queuename = a->argv[5]; 06267 interface = a->argv[3]; 06268 if (a->argc >= 8) { 06269 if (sscanf(a->argv[7], "%d", &penalty) == 1) { 06270 if (penalty < 0) { 06271 ast_cli(a->fd, "Penalty must be >= 0\n"); 06272 penalty = 0; 06273 } 06274 } else { 06275 ast_cli(a->fd, "Penalty must be an integer >= 0\n"); 06276 penalty = 0; 06277 } 06278 } else { 06279 penalty = 0; 06280 } 06281 06282 if (a->argc >= 10) { 06283 membername = a->argv[9]; 06284 } 06285 06286 if (a->argc >= 12) { 06287 state_interface = a->argv[11]; 06288 } 06289 06290 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) { 06291 case RES_OKAY: 06292 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 06293 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 06294 return CLI_SUCCESS; 06295 case RES_EXISTS: 06296 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 06297 return CLI_FAILURE; 06298 case RES_NOSUCHQUEUE: 06299 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 06300 return CLI_FAILURE; 06301 case RES_OUTOFMEMORY: 06302 ast_cli(a->fd, "Out of memory\n"); 06303 return CLI_FAILURE; 06304 case RES_NOT_DYNAMIC: 06305 ast_cli(a->fd, "Member not dynamic\n"); 06306 return CLI_FAILURE; 06307 default: 06308 return CLI_FAILURE; 06309 } 06310 }
static char* handle_queue_pause_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6415 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_pause_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, RESULT_SUCCESS, set_member_paused(), and ast_cli_entry::usage.
06416 { 06417 char *queuename, *interface, *reason; 06418 int paused; 06419 06420 switch (cmd) { 06421 case CLI_INIT: 06422 e->command = "queue {pause|unpause} member"; 06423 e->usage = 06424 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n" 06425 " Pause or unpause a queue member. Not specifying a particular queue\n" 06426 " will pause or unpause a member across all queues to which the member\n" 06427 " belongs.\n"; 06428 return NULL; 06429 case CLI_GENERATE: 06430 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n); 06431 } 06432 06433 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) { 06434 return CLI_SHOWUSAGE; 06435 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) { 06436 return CLI_SHOWUSAGE; 06437 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) { 06438 return CLI_SHOWUSAGE; 06439 } 06440 06441 06442 interface = a->argv[3]; 06443 queuename = a->argc >= 6 ? a->argv[5] : NULL; 06444 reason = a->argc == 8 ? a->argv[7] : NULL; 06445 paused = !strcasecmp(a->argv[1], "pause"); 06446 06447 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) { 06448 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface); 06449 if (!ast_strlen_zero(queuename)) 06450 ast_cli(a->fd, " in queue '%s'", queuename); 06451 if (!ast_strlen_zero(reason)) 06452 ast_cli(a->fd, " for reason '%s'", reason); 06453 ast_cli(a->fd, "\n"); 06454 return CLI_SUCCESS; 06455 } else { 06456 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface); 06457 if (!ast_strlen_zero(queuename)) 06458 ast_cli(a->fd, " in queue '%s'", queuename); 06459 if (!ast_strlen_zero(reason)) 06460 ast_cli(a->fd, " for reason '%s'", reason); 06461 ast_cli(a->fd, "\n"); 06462 return CLI_FAILURE; 06463 } 06464 }
static char* handle_queue_remove_member | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6353 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_remove_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.
06354 { 06355 char *queuename, *interface; 06356 06357 switch (cmd) { 06358 case CLI_INIT: 06359 e->command = "queue remove member"; 06360 e->usage = 06361 "Usage: queue remove member <channel> from <queue>\n" 06362 " Remove a specific channel from a queue.\n"; 06363 return NULL; 06364 case CLI_GENERATE: 06365 return complete_queue_remove_member(a->line, a->word, a->pos, a->n); 06366 } 06367 06368 if (a->argc != 6) { 06369 return CLI_SHOWUSAGE; 06370 } else if (strcmp(a->argv[4], "from")) { 06371 return CLI_SHOWUSAGE; 06372 } 06373 06374 queuename = a->argv[5]; 06375 interface = a->argv[3]; 06376 06377 switch (remove_from_queue(queuename, interface)) { 06378 case RES_OKAY: 06379 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 06380 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 06381 return CLI_SUCCESS; 06382 case RES_EXISTS: 06383 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 06384 return CLI_FAILURE; 06385 case RES_NOSUCHQUEUE: 06386 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 06387 return CLI_FAILURE; 06388 case RES_OUTOFMEMORY: 06389 ast_cli(a->fd, "Out of memory\n"); 06390 return CLI_FAILURE; 06391 default: 06392 return CLI_FAILURE; 06393 } 06394 }
static char* handle_queue_rule_reload | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6585 of file app_queue.c.
References CLI_GENERATE, CLI_INIT, CLI_SUCCESS, ast_cli_entry::command, reload_queue_rules(), and ast_cli_entry::usage.
06586 { 06587 switch (cmd) { 06588 case CLI_INIT: 06589 e->command = "queue rules reload"; 06590 e->usage = 06591 "Usage: queue rules reload\n" 06592 " Reloads rules defined in queuerules.conf\n"; 06593 return NULL; 06594 case CLI_GENERATE: 06595 return NULL; 06596 } 06597 reload_queue_rules(1); 06598 return CLI_SUCCESS; 06599 }
static char* handle_queue_rule_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6551 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.
06552 { 06553 char *rule; 06554 struct rule_list *rl_iter; 06555 struct penalty_rule *pr_iter; 06556 switch (cmd) { 06557 case CLI_INIT: 06558 e->command = "queue rules show"; 06559 e->usage = 06560 "Usage: queue rules show [rulename]\n" 06561 " Show the list of rules associated with rulename. If no\n" 06562 " rulename is specified, list all rules defined in queuerules.conf\n"; 06563 return NULL; 06564 case CLI_GENERATE: 06565 return complete_queue_rule_show(a->line, a->word, a->pos, a->n); 06566 } 06567 06568 if (a->argc != 3 && a->argc != 4) 06569 return CLI_SHOWUSAGE; 06570 06571 rule = a->argc == 4 ? a->argv[3] : ""; 06572 AST_LIST_LOCK(&rule_lists); 06573 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 06574 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) { 06575 ast_cli(a->fd, "Rule: %s\n", rl_iter->name); 06576 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 06577 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); 06578 } 06579 } 06580 } 06581 AST_LIST_UNLOCK(&rule_lists); 06582 return CLI_SUCCESS; 06583 }
static char* handle_queue_set_member_penalty | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 6489 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.
06490 { 06491 char *queuename = NULL, *interface; 06492 int penalty = 0; 06493 06494 switch (cmd) { 06495 case CLI_INIT: 06496 e->command = "queue set penalty"; 06497 e->usage = 06498 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n" 06499 " Set a member's penalty in the queue specified. If no queue is specified\n" 06500 " then that interface's penalty is set in all queues to which that interface is a member\n"; 06501 return NULL; 06502 case CLI_GENERATE: 06503 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n); 06504 } 06505 06506 if (a->argc != 6 && a->argc != 8) { 06507 return CLI_SHOWUSAGE; 06508 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) { 06509 return CLI_SHOWUSAGE; 06510 } 06511 06512 if (a->argc == 8) 06513 queuename = a->argv[7]; 06514 interface = a->argv[5]; 06515 penalty = atoi(a->argv[3]); 06516 06517 switch (set_member_penalty(queuename, interface, penalty)) { 06518 case RESULT_SUCCESS: 06519 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); 06520 return CLI_SUCCESS; 06521 case RESULT_FAILURE: 06522 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); 06523 return CLI_FAILURE; 06524 default: 06525 return CLI_FAILURE; 06526 } 06527 }
static int handle_statechange | ( | void * | datap | ) | [static] |
set a member's status based on device state of that member's interface
Definition at line 767 of file app_queue.c.
References ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdupa, statechange::dev, devstate2str(), member_interface::interface, statechange::state, and update_status().
00768 { 00769 struct member_interface *curint; 00770 struct statechange *sc = datap; 00771 00772 AST_LIST_LOCK(&interfaces); 00773 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00774 char *interface; 00775 char *slash_pos; 00776 interface = ast_strdupa(curint->interface); 00777 if ((slash_pos = strchr(interface, '/'))) 00778 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00779 *slash_pos = '\0'; 00780 00781 if (!strcasecmp(interface, sc->dev)) 00782 break; 00783 } 00784 AST_LIST_UNLOCK(&interfaces); 00785 00786 if (!curint) { 00787 ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, devstate2str(sc->state)); 00788 ast_free(sc); 00789 return 0; 00790 } 00791 00792 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, devstate2str(sc->state)); 00793 00794 update_status(sc->dev, sc->state); 00795 ast_free(sc); 00796 return 0; 00797 }
static void hangupcalls | ( | struct callattempt * | outgoing, | |
struct ast_channel * | exception | |||
) | [static] |
Hang up a list of outgoing calls.
Definition at line 2057 of file app_queue.c.
References ao2_ref, ast_free, ast_hangup(), callattempt::chan, callattempt::member, and callattempt::q_next.
Referenced by try_calling().
02058 { 02059 struct callattempt *oo; 02060 02061 while (outgoing) { 02062 /* Hangup any existing lines we have open */ 02063 if (outgoing->chan && (outgoing->chan != exception)) 02064 ast_hangup(outgoing->chan); 02065 oo = outgoing; 02066 outgoing = outgoing->q_next; 02067 if (oo->member) 02068 ao2_ref(oo->member, -1); 02069 ast_free(oo); 02070 } 02071 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Initialize Queue default values.
Definition at line 883 of file app_queue.c.
References call_queue::announcefrequency, call_queue::announceholdtime, call_queue::announceposition, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ao2_container_alloc, ast_free, AST_LIST_REMOVE_HEAD, ast_str_create(), ast_str_set(), ast_string_field_set, call_queue::autofill, call_queue::dead, DEFAULT_MIN_ANNOUNCE_FREQUENCY, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, penalty_rule::list, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_LINEAR, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, call_queue::rules, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, call_queue::strategy, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by reload_queues().
00884 { 00885 int i; 00886 struct penalty_rule *pr_iter; 00887 00888 q->dead = 0; 00889 q->retry = DEFAULT_RETRY; 00890 q->timeout = DEFAULT_TIMEOUT; 00891 q->maxlen = 0; 00892 q->announcefrequency = 0; 00893 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY; 00894 q->announceholdtime = 1; 00895 q->announcepositionlimit = 10; /* Default 10 positions */ 00896 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */ 00897 q->roundingseconds = 0; /* Default - don't announce seconds */ 00898 q->servicelevel = 0; 00899 q->ringinuse = 1; 00900 q->setinterfacevar = 0; 00901 q->setqueuevar = 0; 00902 q->setqueueentryvar = 0; 00903 q->autofill = autofill_default; 00904 q->montype = montype_default; 00905 q->monfmt[0] = '\0'; 00906 q->reportholdtime = 0; 00907 q->wrapuptime = 0; 00908 q->joinempty = 0; 00909 q->leavewhenempty = 0; 00910 q->memberdelay = 0; 00911 q->maskmemberstatus = 0; 00912 q->eventwhencalled = 0; 00913 q->weight = 0; 00914 q->timeoutrestart = 0; 00915 q->periodicannouncefrequency = 0; 00916 q->randomperiodicannounce = 0; 00917 q->numperiodicannounce = 0; 00918 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 00919 if (!q->members) { 00920 if (q->strategy == QUEUE_STRATEGY_LINEAR) 00921 /* linear strategy depends on order, so we have to place all members in a single bucket */ 00922 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); 00923 else 00924 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 00925 } 00926 q->membercount = 0; 00927 q->found = 1; 00928 00929 ast_string_field_set(q, sound_next, "queue-youarenext"); 00930 ast_string_field_set(q, sound_thereare, "queue-thereare"); 00931 ast_string_field_set(q, sound_calls, "queue-callswaiting"); 00932 ast_string_field_set(q, queue_quantity1, "queue-quantity1"); 00933 ast_string_field_set(q, queue_quantity2, "queue-quantity2"); 00934 ast_string_field_set(q, sound_holdtime, "queue-holdtime"); 00935 ast_string_field_set(q, sound_minutes, "queue-minutes"); 00936 ast_string_field_set(q, sound_minute, "queue-minute"); 00937 ast_string_field_set(q, sound_seconds, "queue-seconds"); 00938 ast_string_field_set(q, sound_thanks, "queue-thankyou"); 00939 ast_string_field_set(q, sound_reporthold, "queue-reporthold"); 00940 00941 if ((q->sound_periodicannounce[0] = ast_str_create(32))) 00942 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce"); 00943 00944 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 00945 if (q->sound_periodicannounce[i]) 00946 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", ""); 00947 } 00948 00949 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) 00950 ast_free(pr_iter); 00951 }
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 635 of file app_queue.c.
References call_queue::head, queue_ent::next, and queue_ent::parent.
Referenced by join_queue().
00636 { 00637 struct queue_ent *cur; 00638 00639 if (!q || !new) 00640 return; 00641 if (prev) { 00642 cur = prev->next; 00643 prev->next = new; 00644 } else { 00645 cur = q->head; 00646 q->head = new; 00647 } 00648 new->next = cur; 00649 new->parent = q; 00650 new->pos = ++(*pos); 00651 new->opos = *pos; 00652 }
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 1055 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_ERROR, LOG_WARNING, rule_list::name, and rule_list::rules.
Referenced by reload_queue_rules().
01056 { 01057 char *timestr, *maxstr, *minstr, *contentdup; 01058 struct penalty_rule *rule = NULL, *rule_iter; 01059 struct rule_list *rl_iter; 01060 int penaltychangetime, inserted = 0; 01061 01062 if (!(rule = ast_calloc(1, sizeof(*rule)))) { 01063 ast_log(LOG_ERROR, "Cannot allocate memory for penaltychange rule at line %d!\n", linenum); 01064 return -1; 01065 } 01066 01067 contentdup = ast_strdupa(content); 01068 01069 if (!(maxstr = strchr(contentdup, ','))) { 01070 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum); 01071 ast_free(rule); 01072 return -1; 01073 } 01074 01075 *maxstr++ = '\0'; 01076 timestr = contentdup; 01077 01078 if ((penaltychangetime = atoi(timestr)) < 0) { 01079 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum); 01080 ast_free(rule); 01081 return -1; 01082 } 01083 01084 rule->time = penaltychangetime; 01085 01086 if ((minstr = strchr(maxstr,','))) 01087 *minstr++ = '\0'; 01088 01089 /* The last check will evaluate true if either no penalty change is indicated for a given rule 01090 * OR if a min penalty change is indicated but no max penalty change is */ 01091 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') { 01092 rule->max_relative = 1; 01093 } 01094 01095 rule->max_value = atoi(maxstr); 01096 01097 if (!ast_strlen_zero(minstr)) { 01098 if (*minstr == '+' || *minstr == '-') 01099 rule->min_relative = 1; 01100 rule->min_value = atoi(minstr); 01101 } else /*there was no minimum specified, so assume this means no change*/ 01102 rule->min_relative = 1; 01103 01104 /*We have the rule made, now we need to insert it where it belongs*/ 01105 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){ 01106 if (strcasecmp(rl_iter->name, list_name)) 01107 continue; 01108 01109 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) { 01110 if (rule->time < rule_iter->time) { 01111 AST_LIST_INSERT_BEFORE_CURRENT(rule, list); 01112 inserted = 1; 01113 break; 01114 } 01115 } 01116 AST_LIST_TRAVERSE_SAFE_END; 01117 01118 if (!inserted) { 01119 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list); 01120 } 01121 } 01122 01123 return 0; 01124 }
static const char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 566 of file app_queue.c.
References ARRAY_LEN, strategy::name, and strategies.
Referenced by __queues_show(), manager_queues_status(), queue_function_var(), and set_queue_variables().
00567 { 00568 int x; 00569 00570 for (x = 0; x < ARRAY_LEN(strategies); x++) { 00571 if (strategy == strategies[x].strategy) 00572 return strategies[x].name; 00573 } 00574 00575 return "<unknown>"; 00576 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static] |
Definition at line 4035 of file app_queue.c.
References 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().
04036 { 04037 struct member *mem; 04038 struct ao2_iterator mem_iter; 04039 04040 if (!q) 04041 return NULL; 04042 04043 mem_iter = ao2_iterator_init(q->members, 0); 04044 while ((mem = ao2_iterator_next(&mem_iter))) { 04045 if (!strcasecmp(interface, mem->interface)) 04046 return mem; 04047 ao2_ref(mem, -1); 04048 } 04049 04050 return NULL; 04051 }
static int interface_exists_global | ( | const char * | interface, | |
int | lock_queue_container | |||
) | [static] |
Definition at line 988 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_copy_string(), F_AO2I_DONTLOCK, member::interface, queue_unref(), queues, and member::state_interface.
Referenced by remove_from_interfaces().
00989 { 00990 struct call_queue *q; 00991 struct member *mem, tmpmem; 00992 struct ao2_iterator queue_iter, mem_iter; 00993 int ret = 0; 00994 00995 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 00996 queue_iter = ao2_iterator_init(queues, lock_queue_container ? 0 : F_AO2I_DONTLOCK); 00997 while ((q = ao2_iterator_next(&queue_iter))) { 00998 ao2_lock(q); 00999 mem_iter = ao2_iterator_init(q->members, 0); 01000 while ((mem = ao2_iterator_next(&mem_iter))) { 01001 if (!strcasecmp(mem->state_interface, interface)) { 01002 ao2_ref(mem, -1); 01003 ret = 1; 01004 break; 01005 } 01006 } 01007 ao2_unlock(q); 01008 queue_unref(q); 01009 } 01010 01011 return ret; 01012 }
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 2833 of file app_queue.c.
References ao2_lock(), ao2_unlock(), ast_debug, queue_ent::chan, call_queue::head, ast_channel::name, queue_ent::next, num_available_members(), queue_ent::parent, and queue_ent::pending.
Referenced by queue_exec(), and wait_our_turn().
02834 { 02835 struct queue_ent *ch; 02836 int res; 02837 int avl; 02838 int idx = 0; 02839 /* This needs a lock. How many members are available to be served? */ 02840 ao2_lock(qe->parent); 02841 02842 avl = num_available_members(qe->parent); 02843 02844 ch = qe->parent->head; 02845 02846 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); 02847 02848 while ((idx < avl) && (ch) && (ch != qe)) { 02849 if (!ch->pending) 02850 idx++; 02851 ch = ch->next; 02852 } 02853 02854 ao2_unlock(qe->parent); 02855 02856 /* If the queue entry is within avl [the number of available members] calls from the top ... */ 02857 if (ch && idx < avl) { 02858 ast_debug(1, "It's our turn (%s).\n", qe->chan->name); 02859 res = 1; 02860 } else { 02861 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); 02862 res = 0; 02863 } 02864 02865 return res; 02866 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason, | |||
const char * | overriding_rule | |||
) | [static] |
Definition at line 1701 of file app_queue.c.
References queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), ast_debug, AST_LIST_FIRST, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, queue_ent::context, copy_rules(), call_queue::count, call_queue::defaultrule, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), manager_event, queue_ent::max_penalty, call_queue::maxlen, queue_ent::min_penalty, queue_ent::moh, ast_channel::name, queue_ent::next, queue_ent::pos, queue_ent::pr, queue_ent::prio, queue_ent::qe_rules, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, queues, S_OR, status, ast_channel::uniqueid, and update_qe_rule().
Referenced by queue_exec().
01702 { 01703 struct call_queue *q; 01704 struct queue_ent *cur, *prev = NULL; 01705 int res = -1; 01706 int pos = 0; 01707 int inserted = 0; 01708 enum queue_member_status status; 01709 int exit = 0; 01710 01711 if (!(q = load_realtime_queue(queuename))) 01712 return res; 01713 01714 ao2_lock(queues); 01715 ao2_lock(q); 01716 01717 copy_rules(qe, S_OR(overriding_rule, q->defaultrule)); 01718 qe->pr = AST_LIST_FIRST(&qe->qe_rules); 01719 01720 /* This is our one */ 01721 while (!exit) { 01722 status = get_member_status(q, qe->max_penalty, qe->min_penalty); 01723 if (!q->joinempty && (status == QUEUE_NO_MEMBERS)) 01724 *reason = QUEUE_JOINEMPTY; 01725 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS)) 01726 *reason = QUEUE_JOINUNAVAIL; 01727 else if ((q->joinempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_MEMBERS)) 01728 *reason = QUEUE_JOINUNAVAIL; 01729 else if (q->maxlen && (q->count >= q->maxlen)) 01730 *reason = QUEUE_FULL; 01731 else { 01732 /* There's space for us, put us at the right position inside 01733 * the queue. 01734 * Take into account the priority of the calling user */ 01735 inserted = 0; 01736 prev = NULL; 01737 cur = q->head; 01738 while (cur) { 01739 /* We have higher priority than the current user, enter 01740 * before him, after all the other users with priority 01741 * higher or equal to our priority. */ 01742 if ((!inserted) && (qe->prio > cur->prio)) { 01743 insert_entry(q, prev, qe, &pos); 01744 inserted = 1; 01745 } 01746 cur->pos = ++pos; 01747 prev = cur; 01748 cur = cur->next; 01749 } 01750 /* No luck, join at the end of the queue */ 01751 if (!inserted) 01752 insert_entry(q, prev, qe, &pos); 01753 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 01754 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 01755 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 01756 q->count++; 01757 res = 0; 01758 manager_event(EVENT_FLAG_CALL, "Join", 01759 "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n", 01760 qe->chan->name, 01761 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */ 01762 S_OR(qe->chan->cid.cid_name, "unknown"), 01763 q->name, qe->pos, q->count, qe->chan->uniqueid ); 01764 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 01765 } 01766 if (!exit && qe->pr && res) { 01767 /* We failed to join the queue, but perhaps we can join if we move 01768 * to the next defined penalty rule 01769 */ 01770 update_qe_rule(qe); 01771 } else { 01772 exit = 1; 01773 } 01774 } 01775 ao2_unlock(q); 01776 ao2_unlock(queues); 01777 01778 return res; 01779 }
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 2000 of file app_queue.c.
References ao2_lock(), ao2_unlink, ao2_unlock(), ast_debug, ast_free, AST_LIST_REMOVE_HEAD, ast_load_realtime(), ast_variables_destroy(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, penalty_rule::list, manager_event, call_queue::name, ast_channel::name, queue_ent::next, queue_ent::parent, queue_ent::pos, queue_ent::qe_rules, queue_ref(), queue_unref(), queues, call_queue::realtime, SENTINEL, ast_channel::uniqueid, and var.
Referenced by try_calling(), and wait_our_turn().
02001 { 02002 struct call_queue *q; 02003 struct queue_ent *current, *prev = NULL; 02004 struct penalty_rule *pr_iter; 02005 int pos = 0; 02006 02007 if (!(q = qe->parent)) 02008 return; 02009 queue_ref(q); 02010 ao2_lock(q); 02011 02012 prev = NULL; 02013 for (current = q->head; current; current = current->next) { 02014 if (current == qe) { 02015 q->count--; 02016 02017 /* Take us out of the queue */ 02018 manager_event(EVENT_FLAG_CALL, "Leave", 02019 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", 02020 qe->chan->name, q->name, q->count, qe->chan->uniqueid); 02021 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 02022 /* Take us out of the queue */ 02023 if (prev) 02024 prev->next = current->next; 02025 else 02026 q->head = current->next; 02027 /* Free penalty rules */ 02028 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) 02029 ast_free(pr_iter); 02030 } else { 02031 /* Renumber the people after us in the queue based on a new count */ 02032 current->pos = ++pos; 02033 prev = current; 02034 } 02035 } 02036 ao2_unlock(q); 02037 02038 /*If the queue is a realtime queue, check to see if it's still defined in real time*/ 02039 if (q->realtime) { 02040 struct ast_variable *var; 02041 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) { 02042 q->dead = 1; 02043 } else { 02044 ast_variables_destroy(var); 02045 } 02046 } 02047 02048 if (q->dead) { 02049 /* It's dead and nobody is in it, so kill it */ 02050 ao2_unlink(queues, q); 02051 } 02052 /* unref the explicit ref earlier in the function */ 02053 queue_unref(q); 02054 }
static int load_module | ( | void | ) | [static] |
Definition at line 6671 of file app_queue.c.
References ao2_container_alloc, aqm_exec(), ast_add_extension2(), ast_cli_register_multiple(), ast_context_find_or_create(), ast_custom_function_register, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), ast_free_ptr, ast_log(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application, ast_strdup, ast_taskprocessor_get(), cli_queue, device_state_cb(), device_state_sub, devicestate_tps, EVENT_FLAG_AGENT, LOG_ERROR, LOG_WARNING, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_log_custom(), manager_queue_member_penalty(), manager_queue_rule_show(), manager_queues_show(), manager_queues_status(), manager_queues_summary(), manager_remove_queue_member(), MAX_QUEUE_BUCKETS, pqm_exec(), ql_exec(), queue_cmp_cb(), queue_exec(), queue_hash_cb(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().
06672 { 06673 int res; 06674 struct ast_context *con; 06675 06676 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb); 06677 06678 if (!reload_queues(0)) 06679 return AST_MODULE_LOAD_DECLINE; 06680 06681 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue"); 06682 if (!con) 06683 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n"); 06684 else 06685 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue"); 06686 06687 if (queue_persistent_members) 06688 reload_queue_members(); 06689 06690 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 06691 res = ast_register_application(app, queue_exec, synopsis, descrip); 06692 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip); 06693 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip); 06694 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip); 06695 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip); 06696 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip); 06697 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); 06698 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); 06699 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary"); 06700 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); 06701 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); 06702 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); 06703 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log"); 06704 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 06705 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules"); 06706 res |= ast_custom_function_register(&queuevar_function); 06707 res |= ast_custom_function_register(&queuemembercount_function); 06708 res |= ast_custom_function_register(&queuemembercount_dep); 06709 res |= ast_custom_function_register(&queuememberlist_function); 06710 res |= ast_custom_function_register(&queuewaitingcount_function); 06711 res |= ast_custom_function_register(&queuememberpenalty_function); 06712 06713 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) { 06714 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); 06715 } 06716 06717 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) { 06718 res = -1; 06719 } 06720 06721 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL); 06722 06723 return res ? AST_MODULE_LOAD_DECLINE : 0; 06724 }
static struct call_queue* load_realtime_queue | ( | const char * | queuename | ) | [static] |
Definition at line 1593 of file app_queue.c.
References ao2_find, ao2_lock(), ao2_unlock(), ast_config_destroy(), ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, OBJ_POINTER, queues, call_queue::realtime, SENTINEL, and update_realtime_members().
Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_qac(), queue_function_qac_dep(), and reload_queue_members().
01594 { 01595 struct ast_variable *queue_vars; 01596 struct ast_config *member_config = NULL; 01597 struct call_queue *q = NULL, tmpq = { 01598 .name = queuename, 01599 }; 01600 01601 /* Find the queue in the in-core list first. */ 01602 q = ao2_find(queues, &tmpq, OBJ_POINTER); 01603 01604 if (!q || q->realtime) { 01605 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all 01606 queue operations while waiting for the DB. 01607 01608 This will be two separate database transactions, so we might 01609 see queue parameters as they were before another process 01610 changed the queue and member list as it was after the change. 01611 Thus we might see an empty member list when a queue is 01612 deleted. In practise, this is unlikely to cause a problem. */ 01613 01614 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL); 01615 if (queue_vars) { 01616 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL); 01617 if (!member_config) { 01618 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n"); 01619 ast_variables_destroy(queue_vars); 01620 return NULL; 01621 } 01622 } 01623 01624 ao2_lock(queues); 01625 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 01626 if (member_config) 01627 ast_config_destroy(member_config); 01628 if (queue_vars) 01629 ast_variables_destroy(queue_vars); 01630 ao2_unlock(queues); 01631 01632 } else { 01633 update_realtime_members(q); 01634 } 01635 return q; 01636 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6052 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, RES_OUTOFMEMORY, and s.
Referenced by load_module().
06053 { 06054 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface; 06055 int paused, penalty = 0; 06056 06057 queuename = astman_get_header(m, "Queue"); 06058 interface = astman_get_header(m, "Interface"); 06059 penalty_s = astman_get_header(m, "Penalty"); 06060 paused_s = astman_get_header(m, "Paused"); 06061 membername = astman_get_header(m, "MemberName"); 06062 state_interface = astman_get_header(m, "StateInterface"); 06063 06064 if (ast_strlen_zero(queuename)) { 06065 astman_send_error(s, m, "'Queue' not specified."); 06066 return 0; 06067 } 06068 06069 if (ast_strlen_zero(interface)) { 06070 astman_send_error(s, m, "'Interface' not specified."); 06071 return 0; 06072 } 06073 06074 if (ast_strlen_zero(penalty_s)) 06075 penalty = 0; 06076 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0) 06077 penalty = 0; 06078 06079 if (ast_strlen_zero(paused_s)) 06080 paused = 0; 06081 else 06082 paused = abs(ast_true(paused_s)); 06083 06084 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) { 06085 case RES_OKAY: 06086 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 06087 astman_send_ack(s, m, "Added interface to queue"); 06088 break; 06089 case RES_EXISTS: 06090 astman_send_error(s, m, "Unable to add interface: Already there"); 06091 break; 06092 case RES_NOSUCHQUEUE: 06093 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 06094 break; 06095 case RES_OUTOFMEMORY: 06096 astman_send_error(s, m, "Out of memory"); 06097 break; 06098 } 06099 06100 return 0; 06101 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6137 of file app_queue.c.
References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_paused().
Referenced by load_module().
06138 { 06139 const char *queuename, *interface, *paused_s, *reason; 06140 int paused; 06141 06142 interface = astman_get_header(m, "Interface"); 06143 paused_s = astman_get_header(m, "Paused"); 06144 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 06145 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */ 06146 06147 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 06148 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 06149 return 0; 06150 } 06151 06152 paused = abs(ast_true(paused_s)); 06153 06154 if (set_member_paused(queuename, interface, reason, paused)) 06155 astman_send_error(s, m, "Interface not found"); 06156 else 06157 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 06158 return 0; 06159 }
static int manager_queue_log_custom | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6161 of file app_queue.c.
References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and S_OR.
Referenced by load_module().
06162 { 06163 const char *queuename, *event, *message, *interface, *uniqueid; 06164 06165 queuename = astman_get_header(m, "Queue"); 06166 uniqueid = astman_get_header(m, "UniqueId"); 06167 interface = astman_get_header(m, "Interface"); 06168 event = astman_get_header(m, "Event"); 06169 message = astman_get_header(m, "Message"); 06170 06171 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) { 06172 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters."); 06173 return 0; 06174 } 06175 06176 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message); 06177 astman_send_ack(s, m, "Event added successfully"); 06178 06179 return 0; 06180 }
static int manager_queue_member_penalty | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6213 of file app_queue.c.
References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_penalty().
Referenced by load_module().
06214 { 06215 const char *queuename, *interface, *penalty_s; 06216 int penalty; 06217 06218 interface = astman_get_header(m, "Interface"); 06219 penalty_s = astman_get_header(m, "Penalty"); 06220 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ 06221 queuename = astman_get_header(m, "Queue"); 06222 06223 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { 06224 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); 06225 return 0; 06226 } 06227 06228 penalty = atoi(penalty_s); 06229 06230 if (set_member_penalty((char *)queuename, (char *)interface, penalty)) 06231 astman_send_error(s, m, "Invalid interface, queuename or penalty"); 06232 else 06233 astman_send_ack(s, m, "Interface penalty set successfully"); 06234 06235 return 0; 06236 }
static int manager_queue_rule_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5862 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, s, and penalty_rule::time.
Referenced by load_module().
05863 { 05864 const char *rule = astman_get_header(m, "Rule"); 05865 struct rule_list *rl_iter; 05866 struct penalty_rule *pr_iter; 05867 05868 AST_LIST_LOCK(&rule_lists); 05869 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 05870 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) { 05871 astman_append(s, "RuleList: %s\r\n", rl_iter->name); 05872 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 05873 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 ); 05874 } 05875 if (!ast_strlen_zero(rule)) 05876 break; 05877 } 05878 } 05879 AST_LIST_UNLOCK(&rule_lists); 05880 05881 astman_append(s, "\r\n\r\n"); 05882 05883 return RESULT_SUCCESS; 05884 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5852 of file app_queue.c.
References __queues_show(), astman_append(), RESULT_SUCCESS, and s.
Referenced by load_module().
05853 { 05854 char *a[] = { "queue", "show" }; 05855 05856 __queues_show(s, -1, 2, a); 05857 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 05858 05859 return RESULT_SUCCESS; 05860 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Queue status info via AMI.
Definition at line 5959 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, member::dynamic, int2strat(), member::interface, member::lastcall, member::membername, ast_channel::name, queue_ent::next, member::paused, member::penalty, queue_unref(), queues, RESULT_SUCCESS, s, S_OR, queue_ent::start, and member::status.
Referenced by load_module().
05960 { 05961 time_t now; 05962 int pos; 05963 const char *id = astman_get_header(m,"ActionID"); 05964 const char *queuefilter = astman_get_header(m,"Queue"); 05965 const char *memberfilter = astman_get_header(m,"Member"); 05966 char idText[256] = ""; 05967 struct call_queue *q; 05968 struct queue_ent *qe; 05969 float sl = 0; 05970 struct member *mem; 05971 struct ao2_iterator queue_iter; 05972 struct ao2_iterator mem_iter; 05973 05974 astman_send_ack(s, m, "Queue status will follow"); 05975 time(&now); 05976 if (!ast_strlen_zero(id)) 05977 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 05978 05979 queue_iter = ao2_iterator_init(queues, 0); 05980 while ((q = ao2_iterator_next(&queue_iter))) { 05981 ao2_lock(q); 05982 05983 /* List queue properties */ 05984 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 05985 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 05986 astman_append(s, "Event: QueueParams\r\n" 05987 "Queue: %s\r\n" 05988 "Max: %d\r\n" 05989 "Strategy: %s\r\n" 05990 "Calls: %d\r\n" 05991 "Holdtime: %d\r\n" 05992 "Completed: %d\r\n" 05993 "Abandoned: %d\r\n" 05994 "ServiceLevel: %d\r\n" 05995 "ServicelevelPerf: %2.1f\r\n" 05996 "Weight: %d\r\n" 05997 "%s" 05998 "\r\n", 05999 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, 06000 q->callsabandoned, q->servicelevel, sl, q->weight, idText); 06001 /* List Queue Members */ 06002 mem_iter = ao2_iterator_init(q->members, 0); 06003 while ((mem = ao2_iterator_next(&mem_iter))) { 06004 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) { 06005 astman_append(s, "Event: QueueMember\r\n" 06006 "Queue: %s\r\n" 06007 "Name: %s\r\n" 06008 "Location: %s\r\n" 06009 "Membership: %s\r\n" 06010 "Penalty: %d\r\n" 06011 "CallsTaken: %d\r\n" 06012 "LastCall: %d\r\n" 06013 "Status: %d\r\n" 06014 "Paused: %d\r\n" 06015 "%s" 06016 "\r\n", 06017 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 06018 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 06019 } 06020 ao2_ref(mem, -1); 06021 } 06022 /* List Queue Entries */ 06023 pos = 1; 06024 for (qe = q->head; qe; qe = qe->next) { 06025 astman_append(s, "Event: QueueEntry\r\n" 06026 "Queue: %s\r\n" 06027 "Position: %d\r\n" 06028 "Channel: %s\r\n" 06029 "CallerIDNum: %s\r\n" 06030 "CallerIDName: %s\r\n" 06031 "Wait: %ld\r\n" 06032 "%s" 06033 "\r\n", 06034 q->name, pos++, qe->chan->name, 06035 S_OR(qe->chan->cid.cid_num, "unknown"), 06036 S_OR(qe->chan->cid.cid_name, "unknown"), 06037 (long) (now - qe->start), idText); 06038 } 06039 } 06040 ao2_unlock(q); 06041 queue_unref(q); 06042 } 06043 06044 astman_append(s, 06045 "Event: QueueStatusComplete\r\n" 06046 "%s" 06047 "\r\n",idText); 06048 06049 return RESULT_SUCCESS; 06050 }
static int manager_queues_summary | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Summary of queue info via the AMI.
Definition at line 5887 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), queue_ent::next, member::paused, queue_unref(), queues, RESULT_SUCCESS, s, queue_ent::start, and member::status.
Referenced by load_module().
05888 { 05889 time_t now; 05890 int qmemcount = 0; 05891 int qmemavail = 0; 05892 int qchancount = 0; 05893 int qlongestholdtime = 0; 05894 const char *id = astman_get_header(m, "ActionID"); 05895 const char *queuefilter = astman_get_header(m, "Queue"); 05896 char idText[256] = ""; 05897 struct call_queue *q; 05898 struct queue_ent *qe; 05899 struct member *mem; 05900 struct ao2_iterator queue_iter; 05901 struct ao2_iterator mem_iter; 05902 05903 astman_send_ack(s, m, "Queue summary will follow"); 05904 time(&now); 05905 if (!ast_strlen_zero(id)) 05906 snprintf(idText, 256, "ActionID: %s\r\n", id); 05907 queue_iter = ao2_iterator_init(queues, 0); 05908 while ((q = ao2_iterator_next(&queue_iter))) { 05909 ao2_lock(q); 05910 05911 /* List queue properties */ 05912 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 05913 /* Reset the necessary local variables if no queuefilter is set*/ 05914 qmemcount = 0; 05915 qmemavail = 0; 05916 qchancount = 0; 05917 qlongestholdtime = 0; 05918 05919 /* List Queue Members */ 05920 mem_iter = ao2_iterator_init(q->members, 0); 05921 while ((mem = ao2_iterator_next(&mem_iter))) { 05922 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) { 05923 ++qmemcount; 05924 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) { 05925 ++qmemavail; 05926 } 05927 } 05928 ao2_ref(mem, -1); 05929 } 05930 for (qe = q->head; qe; qe = qe->next) { 05931 if ((now - qe->start) > qlongestholdtime) { 05932 qlongestholdtime = now - qe->start; 05933 } 05934 ++qchancount; 05935 } 05936 astman_append(s, "Event: QueueSummary\r\n" 05937 "Queue: %s\r\n" 05938 "LoggedIn: %d\r\n" 05939 "Available: %d\r\n" 05940 "Callers: %d\r\n" 05941 "HoldTime: %d\r\n" 05942 "LongestHoldTime: %d\r\n" 05943 "%s" 05944 "\r\n", 05945 q->name, qmemcount, qmemavail, qchancount, q->holdtime, qlongestholdtime, idText); 05946 } 05947 ao2_unlock(q); 05948 queue_unref(q); 05949 } 05950 astman_append(s, 05951 "Event: QueueSummaryComplete\r\n" 05952 "%s" 05953 "\r\n", idText); 05954 05955 return RESULT_SUCCESS; 05956 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 6103 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, RES_OUTOFMEMORY, and s.
Referenced by load_module().
06104 { 06105 const char *queuename, *interface; 06106 06107 queuename = astman_get_header(m, "Queue"); 06108 interface = astman_get_header(m, "Interface"); 06109 06110 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 06111 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 06112 return 0; 06113 } 06114 06115 switch (remove_from_queue(queuename, interface)) { 06116 case RES_OKAY: 06117 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 06118 astman_send_ack(s, m, "Removed interface from queue"); 06119 break; 06120 case RES_EXISTS: 06121 astman_send_error(s, m, "Unable to remove interface: Not there"); 06122 break; 06123 case RES_NOSUCHQUEUE: 06124 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 06125 break; 06126 case RES_OUTOFMEMORY: 06127 astman_send_error(s, m, "Out of memory"); 06128 break; 06129 case RES_NOT_DYNAMIC: 06130 astman_send_error(s, m, "Member not dynamic"); 06131 break; 06132 } 06133 06134 return 0; 06135 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 873 of file app_queue.c.
References CMP_MATCH, CMP_STOP, and member::interface.
Referenced by init_queue().
00874 { 00875 struct member *mem1 = obj1, *mem2 = obj2; 00876 return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP; 00877 }
static int member_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 861 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
00862 { 00863 const struct member *mem = obj; 00864 const char *chname = strchr(mem->interface, '/'); 00865 int ret = 0, i; 00866 if (!chname) 00867 chname = mem->interface; 00868 for (i = 0; i < 5 && chname[i]; i++) 00869 ret += compress_char(chname[i]) << (i * 6); 00870 return ret; 00871 }
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 2081 of file app_queue.c.
References 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().
02082 { 02083 struct member *mem; 02084 int avl = 0; 02085 struct ao2_iterator mem_iter; 02086 02087 mem_iter = ao2_iterator_init(q->members, 0); 02088 while ((mem = ao2_iterator_next(&mem_iter))) { 02089 switch (mem->status) { 02090 case AST_DEVICE_INUSE: 02091 if (!q->ringinuse) 02092 break; 02093 /* else fall through */ 02094 case AST_DEVICE_NOT_INUSE: 02095 case AST_DEVICE_UNKNOWN: 02096 if (!mem->paused) { 02097 avl++; 02098 } 02099 break; 02100 } 02101 ao2_ref(mem, -1); 02102 02103 /* If autofill is not enabled or if the queue's strategy is ringall, then 02104 * we really don't care about the number of available members so much as we 02105 * do that there is at least one available. 02106 * 02107 * In fact, we purposely will return from this function stating that only 02108 * one member is available if either of those conditions hold. That way, 02109 * functions which determine what action to take based on the number of available 02110 * members will operate properly. The reasoning is that even if multiple 02111 * members are available, only the head caller can actually be serviced. 02112 */ 02113 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { 02114 break; 02115 } 02116 } 02117 02118 return avl; 02119 }
static int play_file | ( | struct ast_channel * | chan, | |
const char * | filename | |||
) | [static] |
Definition at line 1781 of file app_queue.c.
References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), queue_ent::chan, and ast_channel::language.
Referenced by say_periodic_announcement(), say_position(), and try_calling().
01782 { 01783 int res; 01784 01785 if (ast_strlen_zero(filename)) { 01786 return 0; 01787 } 01788 01789 ast_stopstream(chan); 01790 01791 res = ast_streamfile(chan, filename, chan->language); 01792 if (!res) 01793 res = ast_waitstream(chan, AST_DIGIT_ANY); 01794 01795 ast_stopstream(chan); 01796 01797 return res; 01798 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
PauseQueueMember application.
Definition at line 4464 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
04465 { 04466 char *parse; 04467 AST_DECLARE_APP_ARGS(args, 04468 AST_APP_ARG(queuename); 04469 AST_APP_ARG(interface); 04470 AST_APP_ARG(options); 04471 AST_APP_ARG(reason); 04472 ); 04473 04474 if (ast_strlen_zero(data)) { 04475 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n"); 04476 return -1; 04477 } 04478 04479 parse = ast_strdupa(data); 04480 04481 AST_STANDARD_APP_ARGS(args, parse); 04482 04483 if (ast_strlen_zero(args.interface)) { 04484 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 04485 return -1; 04486 } 04487 04488 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) { 04489 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 04490 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 04491 return 0; 04492 } 04493 04494 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 04495 04496 return 0; 04497 }
static int ql_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
QueueLog application.
Definition at line 4654 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, and parse().
Referenced by load_module().
04655 { 04656 char *parse; 04657 04658 AST_DECLARE_APP_ARGS(args, 04659 AST_APP_ARG(queuename); 04660 AST_APP_ARG(uniqueid); 04661 AST_APP_ARG(membername); 04662 AST_APP_ARG(event); 04663 AST_APP_ARG(params); 04664 ); 04665 04666 if (ast_strlen_zero(data)) { 04667 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n"); 04668 return -1; 04669 } 04670 04671 parse = ast_strdupa(data); 04672 04673 AST_STANDARD_APP_ARGS(args, parse); 04674 04675 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 04676 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 04677 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n"); 04678 return -1; 04679 } 04680 04681 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 04682 "%s", args.params ? args.params : ""); 04683 04684 return 0; 04685 }
static int queue_cmp_cb | ( | void * | obj, | |
void * | arg, | |||
int | flags | |||
) | [static] |
Definition at line 597 of file app_queue.c.
References CMP_MATCH, CMP_STOP, and call_queue::name.
Referenced by load_module().
00598 { 00599 struct call_queue *q = obj, *q2 = arg; 00600 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0; 00601 }
static int queue_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
The starting point for all queue calls.
The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again unless some condition (such as an expiration time) causes us to exit the queue.
Definition at line 4732 of file app_queue.c.
References AST_APP_ARG, ast_channel_lock, ast_channel_unlock, AST_CONTROL_RINGING, ast_debug, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_moh_start(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_verb, chan, ast_channel::cid, ast_callerid::cid_num, get_member_status(), is_our_turn(), join_queue(), LOG_WARNING, ast_channel::name, parse(), pbx_builtin_getvar_helper(), QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), status, stop, try_calling(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, wait_a_bit(), and wait_our_turn().
Referenced by load_module().
04733 { 04734 int res=-1; 04735 int ringing=0; 04736 const char *user_priority; 04737 const char *max_penalty_str; 04738 const char *min_penalty_str; 04739 int prio; 04740 int qcontinue = 0; 04741 int max_penalty, min_penalty; 04742 enum queue_result reason = QUEUE_UNKNOWN; 04743 /* whether to exit Queue application after the timeout hits */ 04744 int tries = 0; 04745 int noption = 0; 04746 char *parse; 04747 int makeannouncement = 0; 04748 AST_DECLARE_APP_ARGS(args, 04749 AST_APP_ARG(queuename); 04750 AST_APP_ARG(options); 04751 AST_APP_ARG(url); 04752 AST_APP_ARG(announceoverride); 04753 AST_APP_ARG(queuetimeoutstr); 04754 AST_APP_ARG(agi); 04755 AST_APP_ARG(macro); 04756 AST_APP_ARG(gosub); 04757 AST_APP_ARG(rule); 04758 ); 04759 /* Our queue entry */ 04760 struct queue_ent qe; 04761 04762 if (ast_strlen_zero(data)) { 04763 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n"); 04764 return -1; 04765 } 04766 04767 parse = ast_strdupa(data); 04768 AST_STANDARD_APP_ARGS(args, parse); 04769 04770 /* Setup our queue entry */ 04771 memset(&qe, 0, sizeof(qe)); 04772 qe.start = time(NULL); 04773 04774 /* set the expire time based on the supplied timeout; */ 04775 if (!ast_strlen_zero(args.queuetimeoutstr)) 04776 qe.expire = qe.start + atoi(args.queuetimeoutstr); 04777 else 04778 qe.expire = 0; 04779 04780 /* Get the priority from the variable ${QUEUE_PRIO} */ 04781 ast_channel_lock(chan); 04782 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 04783 if (user_priority) { 04784 if (sscanf(user_priority, "%d", &prio) == 1) { 04785 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio); 04786 } else { 04787 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 04788 user_priority, chan->name); 04789 prio = 0; 04790 } 04791 } else { 04792 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n"); 04793 prio = 0; 04794 } 04795 04796 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 04797 04798 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 04799 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) { 04800 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty); 04801 } else { 04802 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 04803 max_penalty_str, chan->name); 04804 max_penalty = 0; 04805 } 04806 } else { 04807 max_penalty = 0; 04808 } 04809 04810 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) { 04811 if (sscanf(min_penalty_str, "%d", &min_penalty) == 1) { 04812 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty); 04813 } else { 04814 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n", 04815 min_penalty_str, chan->name); 04816 min_penalty = 0; 04817 } 04818 } else { 04819 min_penalty = 0; 04820 } 04821 ast_channel_unlock(chan); 04822 04823 if (args.options && (strchr(args.options, 'r'))) 04824 ringing = 1; 04825 04826 if (args.options && (strchr(args.options, 'c'))) 04827 qcontinue = 1; 04828 04829 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 04830 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 04831 04832 qe.chan = chan; 04833 qe.prio = prio; 04834 qe.max_penalty = max_penalty; 04835 qe.min_penalty = min_penalty; 04836 qe.last_pos_said = 0; 04837 qe.last_pos = 0; 04838 qe.last_periodic_announce_time = time(NULL); 04839 qe.last_periodic_announce_sound = 0; 04840 qe.valid_digits = 0; 04841 if (join_queue(args.queuename, &qe, &reason, args.rule)) { 04842 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 04843 set_queue_result(chan, reason); 04844 return 0; 04845 } 04846 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), 04847 S_OR(chan->cid.cid_num, "")); 04848 check_turns: 04849 if (ringing) { 04850 ast_indicate(chan, AST_CONTROL_RINGING); 04851 } else { 04852 ast_moh_start(chan, qe.moh, NULL); 04853 } 04854 04855 /* This is the wait loop for callers 2 through maxlen */ 04856 res = wait_our_turn(&qe, ringing, &reason); 04857 if (res) { 04858 goto stop; 04859 } 04860 04861 makeannouncement = 0; 04862 04863 for (;;) { 04864 /* This is the wait loop for the head caller*/ 04865 /* To exit, they may get their call answered; */ 04866 /* they may dial a digit from the queue context; */ 04867 /* or, they may timeout. */ 04868 04869 enum queue_member_status status = QUEUE_NORMAL; 04870 int exit = 0; 04871 04872 /* Leave if we have exceeded our queuetimeout */ 04873 if (qe.expire && (time(NULL) >= qe.expire)) { 04874 record_abandoned(&qe); 04875 reason = QUEUE_TIMEOUT; 04876 res = 0; 04877 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 04878 qe.pos, qe.opos, (long) time(NULL) - qe.start); 04879 break; 04880 } 04881 04882 if (makeannouncement) { 04883 /* Make a position announcement, if enabled */ 04884 if (qe.parent->announcefrequency) 04885 if ((res = say_position(&qe,ringing))) 04886 goto stop; 04887 } 04888 makeannouncement = 1; 04889 04890 /* Make a periodic announcement, if enabled */ 04891 if (qe.parent->periodicannouncefrequency) 04892 if ((res = say_periodic_announcement(&qe,ringing))) 04893 goto stop; 04894 04895 /* Leave if we have exceeded our queuetimeout */ 04896 if (qe.expire && (time(NULL) >= qe.expire)) { 04897 record_abandoned(&qe); 04898 reason = QUEUE_TIMEOUT; 04899 res = 0; 04900 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04901 break; 04902 } 04903 04904 /* see if we need to move to the next penalty level for this queue */ 04905 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) { 04906 update_qe_rule(&qe); 04907 } 04908 04909 /* Try calling all queue members for 'timeout' seconds */ 04910 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing); 04911 if (res) { 04912 goto stop; 04913 } 04914 04915 /* exit after 'timeout' cycle if 'n' option enabled */ 04916 if (noption && tries >= qe.parent->membercount) { 04917 ast_verb(3, "Exiting on time-out cycle\n"); 04918 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04919 record_abandoned(&qe); 04920 reason = QUEUE_TIMEOUT; 04921 res = 0; 04922 break; 04923 } 04924 04925 for (; !exit || qe.pr; update_qe_rule(&qe)) { 04926 status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty); 04927 04928 if (!qe.pr || status == QUEUE_NORMAL) { 04929 break; 04930 } 04931 04932 if ((qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) || 04933 ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) || 04934 ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) { 04935 continue; 04936 } else { 04937 exit = 1; 04938 } 04939 } 04940 04941 /* leave the queue if no agents, if enabled */ 04942 if (qe.parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) { 04943 record_abandoned(&qe); 04944 reason = QUEUE_LEAVEEMPTY; 04945 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04946 res = 0; 04947 break; 04948 } 04949 04950 /* leave the queue if no reachable agents, if enabled */ 04951 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) { 04952 record_abandoned(&qe); 04953 reason = QUEUE_LEAVEUNAVAIL; 04954 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04955 res = 0; 04956 break; 04957 } 04958 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) { 04959 record_abandoned(&qe); 04960 reason = QUEUE_LEAVEUNAVAIL; 04961 res = 0; 04962 break; 04963 } 04964 04965 /* Leave if we have exceeded our queuetimeout */ 04966 if (qe.expire && (time(NULL) >= qe.expire)) { 04967 record_abandoned(&qe); 04968 reason = QUEUE_TIMEOUT; 04969 res = 0; 04970 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start); 04971 break; 04972 } 04973 04974 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 04975 update_realtime_members(qe.parent); 04976 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 04977 res = wait_a_bit(&qe); 04978 if (res) 04979 goto stop; 04980 04981 /* Since this is a priority queue and 04982 * it is not sure that we are still at the head 04983 * of the queue, go and check for our turn again. 04984 */ 04985 if (!is_our_turn(&qe)) { 04986 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name); 04987 goto check_turns; 04988 } 04989 } 04990 04991 stop: 04992 if (res) { 04993 if (res < 0) { 04994 if (!qe.handled) { 04995 record_abandoned(&qe); 04996 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 04997 "%d|%d|%ld", qe.pos, qe.opos, 04998 (long) time(NULL) - qe.start); 04999 res = -1; 05000 } else if (qcontinue) { 05001 reason = QUEUE_CONTINUE; 05002 res = 0; 05003 } 05004 } else if (qe.valid_digits) { 05005 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 05006 "%s|%d", qe.digits, qe.pos); 05007 } 05008 } 05009 05010 /* Don't allow return code > 0 */ 05011 if (res >= 0) { 05012 res = 0; 05013 if (ringing) { 05014 ast_indicate(chan, -1); 05015 } else { 05016 ast_moh_stop(chan); 05017 } 05018 ast_stopstream(chan); 05019 } 05020 05021 set_queue_variables(qe.parent, qe.chan); 05022 05023 leave_queue(&qe); 05024 if (reason != QUEUE_UNKNOWN) 05025 set_queue_result(chan, reason); 05026 05027 return res; 05028 }
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 5260 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), get_member_penalty(), and LOG_ERROR.
05261 { 05262 int penalty; 05263 AST_DECLARE_APP_ARGS(args, 05264 AST_APP_ARG(queuename); 05265 AST_APP_ARG(interface); 05266 ); 05267 /* Make sure the returned value on error is NULL. */ 05268 buf[0] = '\0'; 05269 05270 if (ast_strlen_zero(data)) { 05271 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05272 return -1; 05273 } 05274 05275 AST_STANDARD_APP_ARGS(args, data); 05276 05277 if (args.argc < 2) { 05278 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05279 return -1; 05280 } 05281 05282 penalty = get_member_penalty (args.queuename, args.interface); 05283 05284 if (penalty >= 0) /* remember that buf is already '\0' */ 05285 snprintf (buf, len, "%d", penalty); 05286 05287 return 0; 05288 }
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 5291 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_ERROR, and set_member_penalty().
05292 { 05293 int penalty; 05294 AST_DECLARE_APP_ARGS(args, 05295 AST_APP_ARG(queuename); 05296 AST_APP_ARG(interface); 05297 ); 05298 05299 if (ast_strlen_zero(data)) { 05300 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05301 return -1; 05302 } 05303 05304 AST_STANDARD_APP_ARGS(args, data); 05305 05306 if (args.argc < 2) { 05307 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05308 return -1; 05309 } 05310 05311 penalty = atoi(value); 05312 05313 if (ast_strlen_zero(args.interface)) { 05314 ast_log (LOG_ERROR, "<interface> parameter can't be null\n"); 05315 return -1; 05316 } 05317 05318 /* if queuename = NULL then penalty will be set for interface in all the queues. */ 05319 if (set_member_penalty(args.queuename, args.interface, penalty)) { 05320 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n"); 05321 return -1; 05322 } 05323 05324 return 0; 05325 }
static int queue_function_qac | ( | struct ast_channel * | chan, | |
const char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Get number either busy / free or total members of a specific queue.
number | of members (busy / free / total) | |
-1 | on error |
Definition at line 5083 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, ast_log(), ast_strlen_zero(), call_queue::count, load_realtime_queue(), LOG_ERROR, LOG_WARNING, member::paused, queue_unref(), and member::status.
05084 { 05085 int count = 0; 05086 struct member *m; 05087 struct ao2_iterator mem_iter; 05088 struct call_queue *q; 05089 char *option; 05090 05091 if (ast_strlen_zero(data)) { 05092 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05093 return -1; 05094 } 05095 05096 if ((option = strchr(data, ','))) 05097 *option++ = '\0'; 05098 else 05099 option = "logged"; 05100 if ((q = load_realtime_queue(data))) { 05101 ao2_lock(q); 05102 if (!strcasecmp(option, "logged")) { 05103 mem_iter = ao2_iterator_init(q->members, 0); 05104 while ((m = ao2_iterator_next(&mem_iter))) { 05105 /* Count the agents who are logged in and presently answering calls */ 05106 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 05107 count++; 05108 } 05109 ao2_ref(m, -1); 05110 } 05111 } else if (!strcasecmp(option, "free")) { 05112 mem_iter = ao2_iterator_init(q->members, 0); 05113 while ((m = ao2_iterator_next(&mem_iter))) { 05114 /* Count the agents who are logged in and presently answering calls */ 05115 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) { 05116 count++; 05117 } 05118 ao2_ref(m, -1); 05119 } 05120 } else /* must be "count" */ 05121 count = q->membercount; 05122 ao2_unlock(q); 05123 queue_unref(q); 05124 } else 05125 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05126 05127 snprintf(buf, len, "%d", count); 05128 05129 return 0; 05130 }
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 5137 of file app_queue.c.
References 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_unref(), and member::status.
05138 { 05139 int count = 0; 05140 struct member *m; 05141 struct call_queue *q; 05142 struct ao2_iterator mem_iter; 05143 static int depflag = 1; 05144 05145 if (depflag) { 05146 depflag = 0; 05147 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"); 05148 } 05149 05150 if (ast_strlen_zero(data)) { 05151 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05152 return -1; 05153 } 05154 05155 if ((q = load_realtime_queue(data))) { 05156 ao2_lock(q); 05157 mem_iter = ao2_iterator_init(q->members, 0); 05158 while ((m = ao2_iterator_next(&mem_iter))) { 05159 /* Count the agents who are logged in and presently answering calls */ 05160 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 05161 count++; 05162 } 05163 ao2_ref(m, -1); 05164 } 05165 ao2_unlock(q); 05166 queue_unref(q); 05167 } else 05168 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05169 05170 snprintf(buf, len, "%d", count); 05171 05172 return 0; 05173 }
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 5212 of file app_queue.c.
References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_log(), ast_strlen_zero(), LOG_ERROR, LOG_WARNING, member::membername, call_queue::members, OBJ_POINTER, queue_unref(), and queues.
05213 { 05214 struct call_queue *q, tmpq = { 05215 .name = data, 05216 }; 05217 struct member *m; 05218 05219 /* Ensure an otherwise empty list doesn't return garbage */ 05220 buf[0] = '\0'; 05221 05222 if (ast_strlen_zero(data)) { 05223 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 05224 return -1; 05225 } 05226 05227 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 05228 int buflen = 0, count = 0; 05229 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 05230 05231 ao2_lock(q); 05232 while ((m = ao2_iterator_next(&mem_iter))) { 05233 /* strcat() is always faster than printf() */ 05234 if (count++) { 05235 strncat(buf + buflen, ",", len - buflen - 1); 05236 buflen++; 05237 } 05238 strncat(buf + buflen, m->membername, len - buflen - 1); 05239 buflen += strlen(m->membername); 05240 /* Safeguard against overflow (negative length) */ 05241 if (buflen >= len - 2) { 05242 ao2_ref(m, -1); 05243 ast_log(LOG_WARNING, "Truncating list\n"); 05244 break; 05245 } 05246 ao2_ref(m, -1); 05247 } 05248 ao2_unlock(q); 05249 queue_unref(q); 05250 } else 05251 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05252 05253 /* We should already be terminated, but let's make sure. */ 05254 buf[len - 1] = '\0'; 05255 05256 return 0; 05257 }
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 5176 of file app_queue.c.
References ao2_find, ao2_lock(), ao2_unlock(), ast_load_realtime(), ast_log(), ast_strlen_zero(), ast_variables_destroy(), LOG_ERROR, LOG_WARNING, OBJ_POINTER, queue_unref(), queues, SENTINEL, and var.
05177 { 05178 int count = 0; 05179 struct call_queue *q, tmpq = { 05180 .name = data, 05181 }; 05182 struct ast_variable *var = NULL; 05183 05184 buf[0] = '\0'; 05185 05186 if (ast_strlen_zero(data)) { 05187 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n"); 05188 return -1; 05189 } 05190 05191 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 05192 ao2_lock(q); 05193 count = q->count; 05194 ao2_unlock(q); 05195 queue_unref(q); 05196 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) { 05197 /* if the queue is realtime but was not found in memory, this 05198 * means that the queue had been deleted from memory since it was 05199 * "dead." This means it has a 0 waiting count 05200 */ 05201 count = 0; 05202 ast_variables_destroy(var); 05203 } else 05204 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05205 05206 snprintf(buf, len, "%d", count); 05207 05208 return 0; 05209 }
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 5035 of file app_queue.c.
References ao2_find, ao2_lock(), ao2_unlock(), ast_log(), ast_strlen_zero(), chan, int2strat(), LOG_ERROR, LOG_WARNING, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_unref(), and queues.
05036 { 05037 int res = -1; 05038 struct call_queue *q, tmpq = { 05039 .name = data, 05040 }; 05041 05042 char interfacevar[256] = ""; 05043 float sl = 0; 05044 05045 if (ast_strlen_zero(data)) { 05046 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05047 return -1; 05048 } 05049 05050 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 05051 ao2_lock(q); 05052 if (q->setqueuevar) { 05053 sl = 0; 05054 res = 0; 05055 05056 if (q->callscompleted > 0) { 05057 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 05058 } 05059 05060 snprintf(interfacevar, sizeof(interfacevar), 05061 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 05062 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 05063 05064 pbx_builtin_setvar_multiple(chan, interfacevar); 05065 } 05066 05067 ao2_unlock(q); 05068 queue_unref(q); 05069 } else { 05070 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05071 } 05072 05073 snprintf(buf, len, "%d", res); 05074 05075 return 0; 05076 }
static int queue_hash_cb | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 590 of file app_queue.c.
References ast_str_case_hash(), and call_queue::name.
Referenced by load_module().
00591 { 00592 const struct call_queue *q = obj; 00593 00594 return ast_str_case_hash(q->name); 00595 }
static struct call_queue* queue_ref | ( | struct call_queue * | q | ) | [inline, static] |
Definition at line 603 of file app_queue.c.
References ao2_ref.
Referenced by leave_queue(), and try_calling().
00604 { 00605 ao2_ref(q, 1); 00606 return q; 00607 }
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 1134 of file app_queue.c.
References queue_ent::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_NO, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, ast_copy_string(), ast_debug, ast_log(), ast_str_create(), ast_str_set(), ast_strdupa, ast_string_field_set, ast_true(), call_queue::autofill, call_queue::autopause, buf, queue_ent::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::minannouncefrequency, call_queue::monfmt, call_queue::montype, call_queue::name, call_queue::numperiodicannounce, call_queue::periodicannouncefrequency, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RINGALL, call_queue::randomperiodicannounce, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::setqueueentryvar, call_queue::setqueuevar, call_queue::sound_periodicannounce, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by reload_queues().
01135 { 01136 if (!strcasecmp(param, "musicclass") || 01137 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 01138 ast_string_field_set(q, moh, val); 01139 } else if (!strcasecmp(param, "announce")) { 01140 ast_string_field_set(q, announce, val); 01141 } else if (!strcasecmp(param, "context")) { 01142 ast_string_field_set(q, context, val); 01143 } else if (!strcasecmp(param, "timeout")) { 01144 q->timeout = atoi(val); 01145 if (q->timeout < 0) 01146 q->timeout = DEFAULT_TIMEOUT; 01147 } else if (!strcasecmp(param, "ringinuse")) { 01148 q->ringinuse = ast_true(val); 01149 } else if (!strcasecmp(param, "setinterfacevar")) { 01150 q->setinterfacevar = ast_true(val); 01151 } else if (!strcasecmp(param, "setqueuevar")) { 01152 q->setqueuevar = ast_true(val); 01153 } else if (!strcasecmp(param, "setqueueentryvar")) { 01154 q->setqueueentryvar = ast_true(val); 01155 } else if (!strcasecmp(param, "monitor-format")) { 01156 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 01157 } else if (!strcasecmp(param, "membermacro")) { 01158 ast_string_field_set(q, membermacro, val); 01159 } else if (!strcasecmp(param, "membergosub")) { 01160 ast_string_field_set(q, membergosub, val); 01161 } else if (!strcasecmp(param, "queue-youarenext")) { 01162 ast_string_field_set(q, sound_next, val); 01163 } else if (!strcasecmp(param, "queue-thereare")) { 01164 ast_string_field_set(q, sound_thereare, val); 01165 } else if (!strcasecmp(param, "queue-callswaiting")) { 01166 ast_string_field_set(q, sound_calls, val); 01167 } else if (!strcasecmp(param, "queue-quantity1")) { 01168 ast_string_field_set(q, queue_quantity1, val); 01169 } else if (!strcasecmp(param, "queue-quantity2")) { 01170 ast_string_field_set(q, queue_quantity2, val); 01171 } else if (!strcasecmp(param, "queue-holdtime")) { 01172 ast_string_field_set(q, sound_holdtime, val); 01173 } else if (!strcasecmp(param, "queue-minutes")) { 01174 ast_string_field_set(q, sound_minutes, val); 01175 } else if (!strcasecmp(param, "queue-minute")) { 01176 ast_string_field_set(q, sound_minute, val); 01177 } else if (!strcasecmp(param, "queue-seconds")) { 01178 ast_string_field_set(q, sound_seconds, val); 01179 } else if (!strcasecmp(param, "queue-thankyou")) { 01180 ast_string_field_set(q, sound_thanks, val); 01181 } else if (!strcasecmp(param, "queue-callerannounce")) { 01182 ast_string_field_set(q, sound_callerannounce, val); 01183 } else if (!strcasecmp(param, "queue-reporthold")) { 01184 ast_string_field_set(q, sound_reporthold, val); 01185 } else if (!strcasecmp(param, "announce-frequency")) { 01186 q->announcefrequency = atoi(val); 01187 } else if (!strcasecmp(param, "min-announce-frequency")) { 01188 q->minannouncefrequency = atoi(val); 01189 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name); 01190 } else if (!strcasecmp(param, "announce-round-seconds")) { 01191 q->roundingseconds = atoi(val); 01192 /* Rounding to any other values just doesn't make sense... */ 01193 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10 01194 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) { 01195 if (linenum >= 0) { 01196 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01197 "using 0 instead for queue '%s' at line %d of queues.conf\n", 01198 val, param, q->name, linenum); 01199 } else { 01200 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01201 "using 0 instead for queue '%s'\n", val, param, q->name); 01202 } 01203 q->roundingseconds=0; 01204 } 01205 } else if (!strcasecmp(param, "announce-holdtime")) { 01206 if (!strcasecmp(val, "once")) 01207 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 01208 else if (ast_true(val)) 01209 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 01210 else 01211 q->announceholdtime = 0; 01212 } else if (!strcasecmp(param, "announce-position")) { 01213 if (!strcasecmp(val, "limit")) 01214 q->announceposition = ANNOUNCEPOSITION_LIMIT; 01215 else if (!strcasecmp(val, "more")) 01216 q->announceposition = ANNOUNCEPOSITION_MORE_THAN; 01217 else if (ast_true(val)) 01218 q->announceposition = ANNOUNCEPOSITION_YES; 01219 else 01220 q->announceposition = ANNOUNCEPOSITION_NO; 01221 } else if (!strcasecmp(param, "announce-position-limit")) { 01222 q->announcepositionlimit = atoi(val); 01223 } else if (!strcasecmp(param, "periodic-announce")) { 01224 if (strchr(val, ',')) { 01225 char *s, *buf = ast_strdupa(val); 01226 unsigned int i = 0; 01227 01228 while ((s = strsep(&buf, ",|"))) { 01229 if (!q->sound_periodicannounce[i]) 01230 q->sound_periodicannounce[i] = ast_str_create(16); 01231 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s); 01232 i++; 01233 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 01234 break; 01235 } 01236 q->numperiodicannounce = i; 01237 } else { 01238 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val); 01239 q->numperiodicannounce = 1; 01240 } 01241 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 01242 q->periodicannouncefrequency = atoi(val); 01243 } else if (!strcasecmp(param, "random-periodic-announce")) { 01244 q->randomperiodicannounce = ast_true(val); 01245 } else if (!strcasecmp(param, "retry")) { 01246 q->retry = atoi(val); 01247 if (q->retry <= 0) 01248 q->retry = DEFAULT_RETRY; 01249 } else if (!strcasecmp(param, "wrapuptime")) { 01250 q->wrapuptime = atoi(val); 01251 } else if (!strcasecmp(param, "autofill")) { 01252 q->autofill = ast_true(val); 01253 } else if (!strcasecmp(param, "monitor-type")) { 01254 if (!strcasecmp(val, "mixmonitor")) 01255 q->montype = 1; 01256 } else if (!strcasecmp(param, "autopause")) { 01257 q->autopause = ast_true(val); 01258 } else if (!strcasecmp(param, "maxlen")) { 01259 q->maxlen = atoi(val); 01260 if (q->maxlen < 0) 01261 q->maxlen = 0; 01262 } else if (!strcasecmp(param, "servicelevel")) { 01263 q->servicelevel= atoi(val); 01264 } else if (!strcasecmp(param, "strategy")) { 01265 int strategy; 01266 01267 /* We are a static queue and already have set this, no need to do it again */ 01268 if (failunknown) { 01269 return; 01270 } 01271 strategy = strat2int(val); 01272 if (strategy < 0) { 01273 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01274 val, q->name); 01275 q->strategy = QUEUE_STRATEGY_RINGALL; 01276 } 01277 if (strategy == q->strategy) { 01278 return; 01279 } 01280 if (strategy == QUEUE_STRATEGY_LINEAR) { 01281 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n"); 01282 return; 01283 } 01284 q->strategy = strategy; 01285 } else if (!strcasecmp(param, "joinempty")) { 01286 if (!strcasecmp(val, "loose")) 01287 q->joinempty = QUEUE_EMPTY_LOOSE; 01288 else if (!strcasecmp(val, "strict")) 01289 q->joinempty = QUEUE_EMPTY_STRICT; 01290 else if (ast_true(val)) 01291 q->joinempty = QUEUE_EMPTY_NORMAL; 01292 else 01293 q->joinempty = 0; 01294 } else if (!strcasecmp(param, "leavewhenempty")) { 01295 if (!strcasecmp(val, "loose")) 01296 q->leavewhenempty = QUEUE_EMPTY_LOOSE; 01297 else if (!strcasecmp(val, "strict")) 01298 q->leavewhenempty = QUEUE_EMPTY_STRICT; 01299 else if (ast_true(val)) 01300 q->leavewhenempty = QUEUE_EMPTY_NORMAL; 01301 else 01302 q->leavewhenempty = 0; 01303 } else if (!strcasecmp(param, "eventmemberstatus")) { 01304 q->maskmemberstatus = !ast_true(val); 01305 } else if (!strcasecmp(param, "eventwhencalled")) { 01306 if (!strcasecmp(val, "vars")) { 01307 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 01308 } else { 01309 q->eventwhencalled = ast_true(val) ? 1 : 0; 01310 } 01311 } else if (!strcasecmp(param, "reportholdtime")) { 01312 q->reportholdtime = ast_true(val); 01313 } else if (!strcasecmp(param, "memberdelay")) { 01314 q->memberdelay = atoi(val); 01315 } else if (!strcasecmp(param, "weight")) { 01316 q->weight = atoi(val); 01317 if (q->weight) 01318 use_weight++; 01319 /* With Realtime queues, if the last queue using weights is deleted in realtime, 01320 we will not see any effect on use_weight until next reload. */ 01321 } else if (!strcasecmp(param, "timeoutrestart")) { 01322 q->timeoutrestart = ast_true(val); 01323 } else if (!strcasecmp(param, "defaultrule")) { 01324 ast_string_field_set(q, defaultrule, val); 01325 } else if (!strcasecmp(param, "timeoutpriority")) { 01326 if (!strcasecmp(val, "conf")) { 01327 q->timeoutpriority = TIMEOUT_PRIORITY_CONF; 01328 } else { 01329 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01330 } 01331 } else if (failunknown) { 01332 if (linenum >= 0) { 01333 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 01334 q->name, param, linenum); 01335 } else { 01336 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 01337 } 01338 } 01339 }
static char* queue_show | ( | struct ast_cli_entry * | e, | |
int | cmd, | |||
struct ast_cli_args * | a | |||
) | [static] |
Definition at line 5833 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.
05834 { 05835 switch ( cmd ) { 05836 case CLI_INIT: 05837 e->command = "queue show"; 05838 e->usage = 05839 "Usage: queue show\n" 05840 " Provides summary information on a specified queue.\n"; 05841 return NULL; 05842 case CLI_GENERATE: 05843 return complete_queue_show(a->line, a->word, a->pos, a->n); 05844 } 05845 05846 return __queues_show(NULL, a->fd, a->argc, a->argv); 05847 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 3164 of file app_queue.c.
References ast_free.
03165 { 03166 struct queue_transfer_ds *qtds = data; 03167 ast_free(qtds); 03168 }
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 3187 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().
03188 { 03189 struct queue_transfer_ds *qtds = data; 03190 struct queue_ent *qe = qtds->qe; 03191 struct member *member = qtds->member; 03192 time_t callstart = qtds->starttime; 03193 int callcompletedinsl = qtds->callcompletedinsl; 03194 struct ast_datastore *datastore; 03195 03196 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 03197 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 03198 (long) (time(NULL) - callstart), qe->opos); 03199 03200 update_queue(qe->parent, member, callcompletedinsl); 03201 03202 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 03203 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 03204 ast_channel_datastore_remove(old_chan, datastore); 03205 } else { 03206 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 03207 } 03208 }
static struct call_queue* queue_unref | ( | struct call_queue * | q | ) | [inline, static] |
Definition at line 609 of file app_queue.c.
References ao2_ref.
Referenced by __queues_show(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), find_queue_by_name_rt(), get_member_penalty(), interface_exists_global(), leave_queue(), manager_queues_status(), manager_queues_summary(), queue_function_qac(), queue_function_qac_dep(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_queues(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_status().
00610 { 00611 ao2_ref(q, -1); 00612 return q; 00613 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 1981 of file app_queue.c.
References ao2_lock(), ao2_unlock(), call_queue::holdtime, and queue_ent::parent.
Referenced by try_calling().
01982 { 01983 int oldvalue; 01984 01985 /* Calculate holdtime using an exponential average */ 01986 /* Thanks to SRT for this contribution */ 01987 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 01988 01989 ao2_lock(qe->parent); 01990 oldvalue = qe->parent->holdtime; 01991 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 01992 ao2_unlock(qe->parent); 01993 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Record that a caller gave up on waiting in queue.
Definition at line 2518 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(), and try_calling().
02519 { 02520 ao2_lock(qe->parent); 02521 set_queue_variables(qe->parent, qe->chan); 02522 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 02523 "Queue: %s\r\n" 02524 "Uniqueid: %s\r\n" 02525 "Position: %d\r\n" 02526 "OriginalPosition: %d\r\n" 02527 "HoldTime: %d\r\n", 02528 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 02529 02530 qe->parent->callsabandoned++; 02531 ao2_unlock(qe->parent); 02532 }
static int reload | ( | void | ) | [static] |
Definition at line 6726 of file app_queue.c.
References ast_unload_realtime(), and reload_queues().
06727 { 06728 ast_unload_realtime("queue_members"); 06729 reload_queues(1); 06730 return 0; 06731 }
static void reload_queue_members | ( | void | ) | [static] |
Reload dynamic queue members persisted into the astdb.
Definition at line 4367 of file app_queue.c.
References add_to_queue(), ao2_find, ao2_lock(), 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, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_unref(), queues, RES_OUTOFMEMORY, member::state_interface, and strsep().
Referenced by load_module().
04368 { 04369 char *cur_ptr; 04370 const char *queue_name; 04371 char *member; 04372 char *interface; 04373 char *membername = NULL; 04374 char *state_interface; 04375 char *penalty_tok; 04376 int penalty = 0; 04377 char *paused_tok; 04378 int paused = 0; 04379 struct ast_db_entry *db_tree; 04380 struct ast_db_entry *entry; 04381 struct call_queue *cur_queue; 04382 char queue_data[PM_MAX_LEN]; 04383 04384 ao2_lock(queues); 04385 04386 /* Each key in 'pm_family' is the name of a queue */ 04387 db_tree = ast_db_gettree(pm_family, NULL); 04388 for (entry = db_tree; entry; entry = entry->next) { 04389 04390 queue_name = entry->key + strlen(pm_family) + 2; 04391 04392 { 04393 struct call_queue tmpq = { 04394 .name = queue_name, 04395 }; 04396 cur_queue = ao2_find(queues, &tmpq, OBJ_POINTER); 04397 } 04398 04399 if (!cur_queue) 04400 cur_queue = load_realtime_queue(queue_name); 04401 04402 if (!cur_queue) { 04403 /* If the queue no longer exists, remove it from the 04404 * database */ 04405 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 04406 ast_db_del(pm_family, queue_name); 04407 continue; 04408 } 04409 04410 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) { 04411 queue_unref(cur_queue); 04412 continue; 04413 } 04414 04415 cur_ptr = queue_data; 04416 while ((member = strsep(&cur_ptr, ",|"))) { 04417 if (ast_strlen_zero(member)) 04418 continue; 04419 04420 interface = strsep(&member, ";"); 04421 penalty_tok = strsep(&member, ";"); 04422 paused_tok = strsep(&member, ";"); 04423 membername = strsep(&member, ";"); 04424 state_interface = strsep(&member, ";"); 04425 04426 if (!penalty_tok) { 04427 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 04428 break; 04429 } 04430 penalty = strtol(penalty_tok, NULL, 10); 04431 if (errno == ERANGE) { 04432 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 04433 break; 04434 } 04435 04436 if (!paused_tok) { 04437 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 04438 break; 04439 } 04440 paused = strtol(paused_tok, NULL, 10); 04441 if ((errno == ERANGE) || paused < 0 || paused > 1) { 04442 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 04443 break; 04444 } 04445 04446 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 04447 04448 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) { 04449 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 04450 break; 04451 } 04452 } 04453 queue_unref(cur_queue); 04454 } 04455 04456 ao2_unlock(queues); 04457 if (db_tree) { 04458 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 04459 ast_db_freetree(db_tree); 04460 } 04461 }
static int reload_queue_rules | ( | int | reload | ) | [static] |
Definition at line 5396 of file app_queue.c.
References ast_calloc, ast_category_browse(), 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_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 handle_queue_rule_reload(), and reload_queues().
05397 { 05398 struct ast_config *cfg; 05399 struct rule_list *rl_iter, *new_rl; 05400 struct penalty_rule *pr_iter; 05401 char *rulecat = NULL; 05402 struct ast_variable *rulevar = NULL; 05403 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 05404 05405 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) { 05406 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n"); 05407 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 05408 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n"); 05409 return AST_MODULE_LOAD_SUCCESS; 05410 } else { 05411 AST_LIST_LOCK(&rule_lists); 05412 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) { 05413 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list))) 05414 ast_free(pr_iter); 05415 ast_free(rl_iter); 05416 } 05417 while ((rulecat = ast_category_browse(cfg, rulecat))) { 05418 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) { 05419 ast_log(LOG_ERROR, "Memory allocation error while loading queuerules.conf! Aborting!\n"); 05420 AST_LIST_UNLOCK(&rule_lists); 05421 return AST_MODULE_LOAD_FAILURE; 05422 } else { 05423 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name)); 05424 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list); 05425 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next) 05426 if(!strcasecmp(rulevar->name, "penaltychange")) { 05427 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno); 05428 } else { 05429 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno); 05430 } 05431 } 05432 } 05433 AST_LIST_UNLOCK(&rule_lists); 05434 } 05435 05436 ast_config_destroy(cfg); 05437 05438 return AST_MODULE_LOAD_SUCCESS; 05439 }
static int reload_queues | ( | int | reload | ) | [static] |
Definition at line 5442 of file app_queue.c.
References add_to_interfaces(), alloc_queue(), ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_load, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_MODULE_LOAD_FAILURE, AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), CONFIG_FLAG_FILEUNCHANGED, config_flags, CONFIG_STATUS_FILEUNCHANGED, create_queue_member(), call_queue::dead, member::delme, member::dynamic, F_AO2I_DONTLOCK, call_queue::found, init_queue(), member::interface, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_unref(), queues, call_queue::realtime, reload_queue_rules(), remove_from_interfaces(), member::state_interface, strat2int(), and var.
Referenced by load_module(), and reload().
05443 { 05444 struct call_queue *q; 05445 struct ast_config *cfg; 05446 char *cat, *tmp; 05447 struct ast_variable *var; 05448 struct member *cur, *newm; 05449 struct ao2_iterator mem_iter; 05450 int new; 05451 const char *general_val = NULL; 05452 char parse[80]; 05453 char *interface, *state_interface; 05454 char *membername = NULL; 05455 int penalty; 05456 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 05457 struct ao2_iterator queue_iter; 05458 AST_DECLARE_APP_ARGS(args, 05459 AST_APP_ARG(interface); 05460 AST_APP_ARG(penalty); 05461 AST_APP_ARG(membername); 05462 AST_APP_ARG(state_interface); 05463 ); 05464 05465 /*First things first. Let's load queuerules.conf*/ 05466 if (reload_queue_rules(reload) == AST_MODULE_LOAD_FAILURE) 05467 return AST_MODULE_LOAD_FAILURE; 05468 05469 if (!(cfg = ast_config_load("queues.conf", config_flags))) { 05470 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 05471 return 0; 05472 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) 05473 return 0; 05474 ao2_lock(queues); 05475 use_weight=0; 05476 /* Mark all queues as dead for the moment */ 05477 queue_iter = ao2_iterator_init(queues, F_AO2I_DONTLOCK); 05478 while ((q = ao2_iterator_next(&queue_iter))) { 05479 if (!q->realtime) { 05480 q->dead = 1; 05481 q->found = 0; 05482 } 05483 queue_unref(q); 05484 } 05485 05486 /* Chug through config file */ 05487 cat = NULL; 05488 while ((cat = ast_category_browse(cfg, cat)) ) { 05489 if (!strcasecmp(cat, "general")) { 05490 /* Initialize global settings */ 05491 queue_keep_stats = 0; 05492 if ((general_val = ast_variable_retrieve(cfg, "general", "keepstats"))) 05493 queue_keep_stats = ast_true(general_val); 05494 queue_persistent_members = 0; 05495 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 05496 queue_persistent_members = ast_true(general_val); 05497 autofill_default = 0; 05498 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 05499 autofill_default = ast_true(general_val); 05500 montype_default = 0; 05501 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) { 05502 if (!strcasecmp(general_val, "mixmonitor")) 05503 montype_default = 1; 05504 } 05505 update_cdr = 0; 05506 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) 05507 update_cdr = ast_true(general_val); 05508 shared_lastcall = 0; 05509 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) 05510 shared_lastcall = ast_true(general_val); 05511 } else { /* Define queue */ 05512 /* Look for an existing one */ 05513 struct call_queue tmpq = { 05514 .name = cat, 05515 }; 05516 if (!(q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 05517 /* Make one then */ 05518 if (!(q = alloc_queue(cat))) { 05519 /* TODO: Handle memory allocation failure */ 05520 } 05521 new = 1; 05522 } else 05523 new = 0; 05524 if (q) { 05525 const char *tmpvar = NULL; 05526 if (!new) 05527 ao2_lock(q); 05528 /* Check if a queue with this name already exists */ 05529 if (q->found) { 05530 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat); 05531 if (!new) { 05532 ao2_unlock(q); 05533 queue_unref(q); 05534 } 05535 continue; 05536 } 05537 /* Due to the fact that the "linear" strategy will have a different allocation 05538 * scheme for queue members, we must devise the queue's strategy before other initializations 05539 */ 05540 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) { 05541 q->strategy = strat2int(tmpvar); 05542 if (q->strategy < 0) { 05543 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 05544 tmpvar, q->name); 05545 q->strategy = QUEUE_STRATEGY_RINGALL; 05546 } 05547 } else 05548 q->strategy = QUEUE_STRATEGY_RINGALL; 05549 /* Re-initialize the queue, and clear statistics */ 05550 init_queue(q); 05551 if (!queue_keep_stats) 05552 clear_queue(q); 05553 mem_iter = ao2_iterator_init(q->members, 0); 05554 while ((cur = ao2_iterator_next(&mem_iter))) { 05555 if (!cur->dynamic) { 05556 cur->delme = 1; 05557 } 05558 ao2_ref(cur, -1); 05559 } 05560 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 05561 if (!strcasecmp(var->name, "member")) { 05562 struct member tmpmem; 05563 membername = NULL; 05564 05565 if (ast_strlen_zero(var->value)) { 05566 ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno); 05567 continue; 05568 } 05569 05570 /* Add a new member */ 05571 ast_copy_string(parse, var->value, sizeof(parse)); 05572 05573 AST_STANDARD_APP_ARGS(args, parse); 05574 05575 interface = args.interface; 05576 if (!ast_strlen_zero(args.penalty)) { 05577 tmp = args.penalty; 05578 while (*tmp && *tmp < 33) tmp++; 05579 penalty = atoi(tmp); 05580 if (penalty < 0) { 05581 penalty = 0; 05582 } 05583 } else 05584 penalty = 0; 05585 05586 if (!ast_strlen_zero(args.membername)) { 05587 membername = args.membername; 05588 while (*membername && *membername < 33) membername++; 05589 } 05590 05591 if (!ast_strlen_zero(args.state_interface)) { 05592 state_interface = args.state_interface; 05593 while (*state_interface && *state_interface < 33) state_interface++; 05594 } else 05595 state_interface = interface; 05596 05597 /* Find the old position in the list */ 05598 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 05599 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 05600 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */ 05601 if (cur && strcasecmp(cur->state_interface, state_interface)) { 05602 remove_from_interfaces(cur->state_interface, 0); 05603 } 05604 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface); 05605 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) 05606 add_to_interfaces(state_interface); 05607 ao2_link(q->members, newm); 05608 ao2_ref(newm, -1); 05609 newm = NULL; 05610 05611 if (cur) 05612 ao2_ref(cur, -1); 05613 else { 05614 q->membercount++; 05615 } 05616 } else { 05617 queue_set_param(q, var->name, var->value, var->lineno, 1); 05618 } 05619 } 05620 05621 /* Free remaining members marked as delme */ 05622 mem_iter = ao2_iterator_init(q->members, 0); 05623 while ((cur = ao2_iterator_next(&mem_iter))) { 05624 if (! cur->delme) { 05625 ao2_ref(cur, -1); 05626 continue; 05627 } 05628 q->membercount--; 05629 ao2_unlink(q->members, cur); 05630 remove_from_interfaces(cur->interface, 0); 05631 ao2_ref(cur, -1); 05632 } 05633 05634 if (new) { 05635 ao2_link(queues, q); 05636 } else 05637 ao2_unlock(q); 05638 queue_unref(q); 05639 } 05640 } 05641 } 05642 ast_config_destroy(cfg); 05643 queue_iter = ao2_iterator_init(queues, 0); 05644 while ((q = ao2_iterator_next(&queue_iter))) { 05645 if (q->dead) { 05646 ao2_unlink(queues, q); 05647 } else { 05648 ao2_lock(q); 05649 mem_iter = ao2_iterator_init(q->members, 0); 05650 while ((cur = ao2_iterator_next(&mem_iter))) { 05651 if (cur->dynamic) 05652 q->membercount++; 05653 cur->status = ast_device_state(cur->state_interface); 05654 ao2_ref(cur, -1); 05655 } 05656 ao2_unlock(q); 05657 } 05658 queue_unref(q); 05659 } 05660 ao2_unlock(queues); 05661 return 1; 05662 }
static int remove_from_interfaces | ( | const char * | interface, | |
int | lock_queue_container | |||
) | [static] |
Definition at line 1014 of file app_queue.c.
References ast_debug, ast_free, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, member_interface::interface, interface_exists_global(), and member_interface::list.
Referenced by free_members(), reload_queues(), remove_from_queue(), rt_handle_member_record(), and update_realtime_members().
01015 { 01016 struct member_interface *curint; 01017 01018 if (interface_exists_global(interface, lock_queue_container)) 01019 return 0; 01020 01021 AST_LIST_LOCK(&interfaces); 01022 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) { 01023 if (!strcasecmp(curint->interface, interface)) { 01024 ast_debug(1, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface); 01025 AST_LIST_REMOVE_CURRENT(list); 01026 ast_free(curint); 01027 break; 01028 } 01029 } 01030 AST_LIST_TRAVERSE_SAFE_END; 01031 AST_LIST_UNLOCK(&interfaces); 01032 01033 return 0; 01034 }
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 4104 of file app_queue.c.
References ao2_find, ao2_lock(), ao2_ref, 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_unref(), queues, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::state_interface.
Referenced by attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().
04105 { 04106 struct call_queue *q, tmpq = { 04107 .name = queuename, 04108 }; 04109 struct member *mem, tmpmem; 04110 int res = RES_NOSUCHQUEUE; 04111 04112 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04113 if ((q = ao2_find(queues, &tmpq, OBJ_POINTER))) { 04114 ao2_lock(queues); 04115 ao2_lock(q); 04116 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 04117 /* XXX future changes should beware of this assumption!! */ 04118 if (!mem->dynamic) { 04119 ao2_ref(mem, -1); 04120 ao2_unlock(q); 04121 queue_unref(q); 04122 ao2_unlock(queues); 04123 return RES_NOT_DYNAMIC; 04124 } 04125 q->membercount--; 04126 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 04127 "Queue: %s\r\n" 04128 "Location: %s\r\n" 04129 "MemberName: %s\r\n", 04130 q->name, mem->interface, mem->membername); 04131 ao2_unlink(q->members, mem); 04132 remove_from_interfaces(mem->state_interface, 0); 04133 ao2_ref(mem, -1); 04134 04135 if (queue_persistent_members) 04136 dump_queue_members(q); 04137 04138 res = RES_OKAY; 04139 } else { 04140 res = RES_EXISTS; 04141 } 04142 ao2_unlock(q); 04143 ao2_unlock(queues); 04144 queue_unref(q); 04145 } 04146 04147 return res; 04148 }
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 2218 of file app_queue.c.
References ast_channel::adsicpe, ao2_lock(), ao2_unlock(), ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), ast_debug, AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_free, ast_request(), ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, compare_weight(), ast_channel::context, ast_channel::data, dialcontext, do_hang(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, callattempt::interface, callattempt::lastcall, callattempt::lastqueue, queue_ent::linpos, manager_event, callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, member::state_interface, member::status, status, callattempt::stillgoing, ast_channel::uniqueid, update_status(), vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.
Referenced by ring_one().
02219 { 02220 int res; 02221 int status; 02222 char tech[256]; 02223 char *location; 02224 const char *macrocontext, *macroexten; 02225 02226 /* on entry here, we know that tmp->chan == NULL */ 02227 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) || 02228 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) { 02229 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 02230 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface); 02231 if (qe->chan->cdr) 02232 ast_cdr_busy(qe->chan->cdr); 02233 tmp->stillgoing = 0; 02234 (*busies)++; 02235 return 0; 02236 } 02237 02238 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 02239 ast_debug(1, "%s in use, can't receive call\n", tmp->interface); 02240 if (qe->chan->cdr) 02241 ast_cdr_busy(qe->chan->cdr); 02242 tmp->stillgoing = 0; 02243 return 0; 02244 } 02245 02246 if (tmp->member->paused) { 02247 ast_debug(1, "%s paused, can't receive call\n", tmp->interface); 02248 if (qe->chan->cdr) 02249 ast_cdr_busy(qe->chan->cdr); 02250 tmp->stillgoing = 0; 02251 return 0; 02252 } 02253 if (use_weight && compare_weight(qe->parent,tmp->member)) { 02254 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 02255 if (qe->chan->cdr) 02256 ast_cdr_busy(qe->chan->cdr); 02257 tmp->stillgoing = 0; 02258 (*busies)++; 02259 return 0; 02260 } 02261 02262 ast_copy_string(tech, tmp->interface, sizeof(tech)); 02263 if ((location = strchr(tech, '/'))) 02264 *location++ = '\0'; 02265 else 02266 location = ""; 02267 02268 /* Request the peer */ 02269 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); 02270 if (!tmp->chan) { /* If we can't, just go on to the next call */ 02271 if (qe->chan->cdr) 02272 ast_cdr_busy(qe->chan->cdr); 02273 tmp->stillgoing = 0; 02274 02275 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface)); 02276 02277 ao2_lock(qe->parent); 02278 qe->parent->rrpos++; 02279 qe->linpos++; 02280 ao2_unlock(qe->parent); 02281 02282 02283 (*busies)++; 02284 return 0; 02285 } 02286 02287 tmp->chan->appl = "AppQueue"; 02288 tmp->chan->data = "(Outgoing Line)"; 02289 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); 02290 if (tmp->chan->cid.cid_num) 02291 ast_free(tmp->chan->cid.cid_num); 02292 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); 02293 if (tmp->chan->cid.cid_name) 02294 ast_free(tmp->chan->cid.cid_name); 02295 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); 02296 if (tmp->chan->cid.cid_ani) 02297 ast_free(tmp->chan->cid.cid_ani); 02298 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); 02299 02300 /* Inherit specially named variables from parent channel */ 02301 ast_channel_inherit_variables(qe->chan, tmp->chan); 02302 02303 /* Presense of ADSI CPE on outgoing channel follows ours */ 02304 tmp->chan->adsicpe = qe->chan->adsicpe; 02305 02306 /* Inherit context and extension */ 02307 ast_channel_lock(qe->chan); 02308 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 02309 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); 02310 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 02311 if (!ast_strlen_zero(macroexten)) 02312 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 02313 else 02314 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 02315 ast_channel_unlock(qe->chan); 02316 02317 /* Place the call, but don't wait on the answer */ 02318 if ((res = ast_call(tmp->chan, location, 0))) { 02319 /* Again, keep going even if there's an error */ 02320 ast_debug(1, "ast call on peer returned %d\n", res); 02321 ast_verb(3, "Couldn't call %s\n", tmp->interface); 02322 do_hang(tmp); 02323 (*busies)++; 02324 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface)); 02325 return 0; 02326 } else if (qe->parent->eventwhencalled) { 02327 char vars[2048]; 02328 02329 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 02330 "Queue: %s\r\n" 02331 "AgentCalled: %s\r\n" 02332 "AgentName: %s\r\n" 02333 "ChannelCalling: %s\r\n" 02334 "DestinationChannel: %s\r\n" 02335 "CallerIDNum: %s\r\n" 02336 "CallerIDName: %s\r\n" 02337 "Context: %s\r\n" 02338 "Extension: %s\r\n" 02339 "Priority: %d\r\n" 02340 "Uniqueid: %s\r\n" 02341 "%s", 02342 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name, 02343 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", 02344 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", 02345 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid, 02346 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 02347 ast_verb(3, "Called %s\n", tmp->interface); 02348 } 02349 02350 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface)); 02351 return 1; 02352 }
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 2380 of file app_queue.c.
References ast_debug, callattempt::chan, queue_ent::expire, find_best(), callattempt::interface, callattempt::metric, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_entry(), callattempt::stillgoing, and call_queue::strategy.
Referenced by try_calling(), and wait_for_answer().
02381 { 02382 int ret = 0; 02383 02384 while (ret == 0) { 02385 struct callattempt *best = find_best(outgoing); 02386 if (!best) { 02387 ast_debug(1, "Nobody left to try ringing in queue\n"); 02388 break; 02389 } 02390 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 02391 struct callattempt *cur; 02392 /* Ring everyone who shares this best metric (for ringall) */ 02393 for (cur = outgoing; cur; cur = cur->q_next) { 02394 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 02395 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 02396 ret |= ring_entry(qe, cur, busies); 02397 } 02398 } 02399 } else { 02400 /* Ring just the best channel */ 02401 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric); 02402 ret = ring_entry(qe, best, busies); 02403 } 02404 02405 /* If we have timed out, break out */ 02406 if (qe->expire && (time(NULL) >= qe->expire)) { 02407 ast_debug(1, "Queue timed out while ringing members.\n"); 02408 ret = 0; 02409 break; 02410 } 02411 } 02412 02413 return ret; 02414 }
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 2535 of file app_queue.c.
References ast_queue_log(), ast_verb, call_queue::autopause, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, manager_event, ast_channel::name, call_queue::name, queue_ent::parent, set_member_paused(), and ast_channel::uniqueid.
02536 { 02537 ast_verb(3, "Nobody picked up in %d ms\n", rnatime); 02538 if (qe->parent->eventwhencalled) 02539 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer", 02540 "Queue: %s\r\n" 02541 "Uniqueid: %s\r\n" 02542 "Channel: %s\r\n" 02543 "Member: %s\r\n" 02544 "MemberName: %s\r\n" 02545 "Ringtime: %d\r\n", 02546 qe->parent->name, 02547 qe->chan->uniqueid, 02548 qe->chan->name, 02549 interface, 02550 membername, 02551 rnatime); 02552 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 02553 if (qe->parent->autopause && pause) { 02554 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { 02555 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); 02556 } else { 02557 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 02558 } 02559 } 02560 return; 02561 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
RemoveQueueMember application.
Definition at line 4536 of file app_queue.c.
References AST_APP_ARG, ast_debug, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, 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().
04537 { 04538 int res=-1; 04539 char *parse, *temppos = NULL; 04540 AST_DECLARE_APP_ARGS(args, 04541 AST_APP_ARG(queuename); 04542 AST_APP_ARG(interface); 04543 AST_APP_ARG(options); 04544 ); 04545 04546 04547 if (ast_strlen_zero(data)) { 04548 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n"); 04549 return -1; 04550 } 04551 04552 parse = ast_strdupa(data); 04553 04554 AST_STANDARD_APP_ARGS(args, parse); 04555 04556 if (ast_strlen_zero(args.interface)) { 04557 args.interface = ast_strdupa(chan->name); 04558 temppos = strrchr(args.interface, '-'); 04559 if (temppos) 04560 *temppos = '\0'; 04561 } 04562 04563 switch (remove_from_queue(args.queuename, args.interface)) { 04564 case RES_OKAY: 04565 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 04566 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 04567 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 04568 res = 0; 04569 break; 04570 case RES_EXISTS: 04571 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 04572 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 04573 res = 0; 04574 break; 04575 case RES_NOSUCHQUEUE: 04576 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 04577 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 04578 res = 0; 04579 break; 04580 case RES_NOT_DYNAMIC: 04581 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 04582 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 04583 res = 0; 04584 break; 04585 } 04586 04587 return res; 04588 }
static void rt_handle_member_record | ( | struct call_queue * | q, | |
char * | interface, | |||
const char * | rt_uniqueid, | |||
const char * | membername, | |||
const char * | penalty_str, | |||
const char * | paused_str, | |||
const char * | state_interface | |||
) | [static] |
Find rt member record to update otherwise create one.
Search for member in queue, if found update penalty/paused state, if no memeber exists create one flag it as a RT member and add to queue member list.
Definition at line 1347 of file app_queue.c.
References add_to_interfaces(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_queue_log(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, remove_from_interfaces(), member::rt_uniqueid, and member::state_interface.
Referenced by update_realtime_members().
01348 { 01349 struct member *m; 01350 struct ao2_iterator mem_iter; 01351 int penalty = 0; 01352 int paused = 0; 01353 int found = 0; 01354 01355 if (penalty_str) { 01356 penalty = atoi(penalty_str); 01357 if (penalty < 0) 01358 penalty = 0; 01359 } 01360 01361 if (paused_str) { 01362 paused = atoi(paused_str); 01363 if (paused < 0) 01364 paused = 0; 01365 } 01366 01367 /* Find member by realtime uniqueid and update */ 01368 mem_iter = ao2_iterator_init(q->members, 0); 01369 while ((m = ao2_iterator_next(&mem_iter))) { 01370 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) { 01371 m->dead = 0; /* Do not delete this one. */ 01372 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 01373 if (paused_str) 01374 m->paused = paused; 01375 if (strcasecmp(state_interface, m->state_interface)) { 01376 remove_from_interfaces(m->state_interface, 0); 01377 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); 01378 add_to_interfaces(m->state_interface); 01379 } 01380 m->penalty = penalty; 01381 found = 1; 01382 ao2_ref(m, -1); 01383 break; 01384 } 01385 ao2_ref(m, -1); 01386 } 01387 01388 /* Create a new member */ 01389 if (!found) { 01390 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { 01391 m->dead = 0; 01392 m->realtime = 1; 01393 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 01394 add_to_interfaces(m->state_interface); 01395 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", ""); 01396 ao2_link(q->members, m); 01397 ao2_ref(m, -1); 01398 m = NULL; 01399 q->membercount++; 01400 } 01401 } 01402 }
static int say_periodic_announcement | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Playback announcement to queued members if peroid has elapsed.
Definition at line 2465 of file app_queue.c.
References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_random(), ast_strlen_zero(), ast_verb, queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::moh, call_queue::numperiodicannounce, queue_ent::parent, call_queue::periodicannouncefrequency, play_file(), call_queue::randomperiodicannounce, call_queue::sound_periodicannounce, and valid_exit().
Referenced by queue_exec(), and wait_our_turn().
02466 { 02467 int res = 0; 02468 time_t now; 02469 02470 /* Get the current time */ 02471 time(&now); 02472 02473 /* Check to see if it is time to announce */ 02474 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 02475 return 0; 02476 02477 /* Stop the music on hold so we can play our own file */ 02478 if (ringing) 02479 ast_indicate(qe->chan,-1); 02480 else 02481 ast_moh_stop(qe->chan); 02482 02483 ast_verb(3, "Playing periodic announcement\n"); 02484 02485 if (qe->parent->randomperiodicannounce) { 02486 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce; 02487 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 02488 ast_strlen_zero(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str)) { 02489 qe->last_periodic_announce_sound = 0; 02490 } 02491 02492 /* play the announcement */ 02493 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]->str); 02494 02495 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 02496 res = 0; 02497 02498 /* Resume Music on Hold if the caller is going to stay in the queue */ 02499 if (!res) { 02500 if (ringing) 02501 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02502 else 02503 ast_moh_start(qe->chan, qe->moh, NULL); 02504 } 02505 02506 /* update last_periodic_announce_time */ 02507 qe->last_periodic_announce_time = now; 02508 02509 /* Update the current periodic announcement to the next announcement */ 02510 if (!qe->parent->randomperiodicannounce) { 02511 qe->last_periodic_announce_sound++; 02512 } 02513 02514 return res; 02515 }
static int say_position | ( | struct queue_ent * | qe, | |
int | ringing | |||
) | [static] |
Definition at line 1838 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(), and wait_our_turn().
01839 { 01840 int res = 0, avgholdmins, avgholdsecs, announceposition = 0; 01841 int say_thanks = 1; 01842 time_t now; 01843 01844 /* Let minannouncefrequency seconds pass between the start of each position announcement */ 01845 time(&now); 01846 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) 01847 return 0; 01848 01849 /* If either our position has changed, or we are over the freq timer, say position */ 01850 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 01851 return 0; 01852 01853 if (ringing) { 01854 ast_indicate(qe->chan,-1); 01855 } else { 01856 ast_moh_stop(qe->chan); 01857 } 01858 01859 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES || 01860 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN || 01861 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT && 01862 qe->pos <= qe->parent->announcepositionlimit)) 01863 announceposition = 1; 01864 01865 01866 if (announceposition == 1) { 01867 /* Say we're next, if we are */ 01868 if (qe->pos == 1) { 01869 res = play_file(qe->chan, qe->parent->sound_next); 01870 if (res) 01871 goto playout; 01872 else 01873 goto posout; 01874 } else { 01875 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 01876 /* More than Case*/ 01877 res = play_file(qe->chan, qe->parent->queue_quantity1); 01878 if (res) 01879 goto playout; 01880 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 01881 if (res) 01882 goto playout; 01883 } else { 01884 /* Normal Case */ 01885 res = play_file(qe->chan, qe->parent->sound_thereare); 01886 if (res) 01887 goto playout; 01888 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 01889 if (res) 01890 goto playout; 01891 } 01892 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 01893 /* More than Case*/ 01894 res = play_file(qe->chan, qe->parent->queue_quantity2); 01895 if (res) 01896 goto playout; 01897 } else { 01898 res = play_file(qe->chan, qe->parent->sound_calls); 01899 if (res) 01900 goto playout; 01901 } 01902 } 01903 } 01904 /* Round hold time to nearest minute */ 01905 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 01906 01907 /* If they have specified a rounding then round the seconds as well */ 01908 if (qe->parent->roundingseconds) { 01909 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 01910 avgholdsecs *= qe->parent->roundingseconds; 01911 } else { 01912 avgholdsecs = 0; 01913 } 01914 01915 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 01916 01917 /* If the hold time is >1 min, if it's enabled, and if it's not 01918 supposed to be only once and we have already said it, say it */ 01919 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 01920 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 01921 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 01922 res = play_file(qe->chan, qe->parent->sound_holdtime); 01923 if (res) 01924 goto playout; 01925 01926 if (avgholdmins >= 1) { 01927 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 01928 if (res) 01929 goto playout; 01930 01931 if (avgholdmins == 1) { 01932 res = play_file(qe->chan, qe->parent->sound_minute); 01933 if (res) 01934 goto playout; 01935 } else { 01936 res = play_file(qe->chan, qe->parent->sound_minutes); 01937 if (res) 01938 goto playout; 01939 } 01940 } 01941 if (avgholdsecs >= 1) { 01942 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 01943 if (res) 01944 goto playout; 01945 01946 res = play_file(qe->chan, qe->parent->sound_seconds); 01947 if (res) 01948 goto playout; 01949 } 01950 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) { 01951 say_thanks = 0; 01952 } 01953 01954 posout: 01955 if (qe->parent->announceposition) { 01956 ast_verb(3, "Told %s in %s their queue position (which was %d)\n", 01957 qe->chan->name, qe->parent->name, qe->pos); 01958 } 01959 if (say_thanks) { 01960 res = play_file(qe->chan, qe->parent->sound_thanks); 01961 } 01962 playout: 01963 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 01964 res = 0; 01965 01966 /* Set our last_pos indicators */ 01967 qe->last_pos = now; 01968 qe->last_pos_said = qe->pos; 01969 01970 /* Don't restart music on hold if we're about to exit the caller from the queue */ 01971 if (!res) { 01972 if (ringing) { 01973 ast_indicate(qe->chan, AST_CONTROL_RINGING); 01974 } else { 01975 ast_moh_start(qe->chan, qe->moh, NULL); 01976 } 01977 } 01978 return res; 01979 }
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 3121 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().
Referenced by try_calling().
03124 { 03125 const char *reason = NULL; /* silence dumb compilers */ 03126 03127 if (!qe->parent->eventwhencalled) 03128 return; 03129 03130 switch (rsn) { 03131 case CALLER: 03132 reason = "caller"; 03133 break; 03134 case AGENT: 03135 reason = "agent"; 03136 break; 03137 case TRANSFER: 03138 reason = "transfer"; 03139 break; 03140 } 03141 03142 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03143 "Queue: %s\r\n" 03144 "Uniqueid: %s\r\n" 03145 "Channel: %s\r\n" 03146 "Member: %s\r\n" 03147 "MemberName: %s\r\n" 03148 "HoldTime: %ld\r\n" 03149 "TalkTime: %ld\r\n" 03150 "Reason: %s\r\n" 03151 "%s", 03152 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03153 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason, 03154 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : ""); 03155 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
const char * | reason, | |||
int | paused | |||
) | [static] |
Definition at line 4212 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_WARNING, manager_event, member::membername, member::paused, queue_unref(), queues, member::realtime, RESULT_FAILURE, RESULT_SUCCESS, S_OR, and update_realtime_member_field().
Referenced by handle_queue_pause_member(), manager_pause_queue_member(), pqm_exec(), rna(), and upqm_exec().
04213 { 04214 int found = 0; 04215 struct call_queue *q; 04216 struct member *mem; 04217 struct ao2_iterator queue_iter; 04218 int failed; 04219 04220 /* Special event for when all queues are paused - individual events still generated */ 04221 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 04222 if (ast_strlen_zero(queuename)) 04223 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 04224 04225 queue_iter = ao2_iterator_init(queues, 0); 04226 while ((q = ao2_iterator_next(&queue_iter))) { 04227 ao2_lock(q); 04228 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 04229 if ((mem = interface_exists(q, interface))) { 04230 if (mem->paused == paused) { 04231 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 04232 } 04233 04234 failed = 0; 04235 if (mem->realtime) { 04236 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 04237 } 04238 04239 if (failed) { 04240 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface); 04241 ao2_ref(mem, -1); 04242 ao2_unlock(q); 04243 continue; 04244 } 04245 found++; 04246 mem->paused = paused; 04247 04248 if (queue_persistent_members) 04249 dump_queue_members(q); 04250 04251 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, "")); 04252 04253 if (!ast_strlen_zero(reason)) { 04254 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 04255 "Queue: %s\r\n" 04256 "Location: %s\r\n" 04257 "MemberName: %s\r\n" 04258 "Paused: %d\r\n" 04259 "Reason: %s\r\n", 04260 q->name, mem->interface, mem->membername, paused, reason); 04261 } else { 04262 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 04263 "Queue: %s\r\n" 04264 "Location: %s\r\n" 04265 "MemberName: %s\r\n" 04266 "Paused: %d\r\n", 04267 q->name, mem->interface, mem->membername, paused); 04268 } 04269 ao2_ref(mem, -1); 04270 } 04271 } 04272 04273 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) { 04274 ao2_unlock(q); 04275 queue_unref(q); 04276 break; 04277 } 04278 04279 ao2_unlock(q); 04280 queue_unref(q); 04281 } 04282 04283 return found ? RESULT_SUCCESS : RESULT_FAILURE; 04284 }
static int set_member_penalty | ( | char * | queuename, | |
char * | interface, | |||
int | penalty | |||
) | [static] |
Definition at line 4287 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_log(), ast_queue_log(), ast_strlen_zero(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_ERROR, manager_event, member::penalty, queue_unref(), queues, RESULT_FAILURE, and RESULT_SUCCESS.
Referenced by handle_queue_set_member_penalty(), manager_queue_member_penalty(), and queue_function_memberpenalty_write().
04288 { 04289 int foundinterface = 0, foundqueue = 0; 04290 struct call_queue *q; 04291 struct member *mem; 04292 struct ao2_iterator queue_iter; 04293 04294 if (penalty < 0) { 04295 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); 04296 return RESULT_FAILURE; 04297 } 04298 04299 queue_iter = ao2_iterator_init(queues, 0); 04300 while ((q = ao2_iterator_next(&queue_iter))) { 04301 ao2_lock(q); 04302 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 04303 foundqueue++; 04304 if ((mem = interface_exists(q, interface))) { 04305 foundinterface++; 04306 mem->penalty = penalty; 04307 04308 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); 04309 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", 04310 "Queue: %s\r\n" 04311 "Location: %s\r\n" 04312 "Penalty: %d\r\n", 04313 q->name, mem->interface, penalty); 04314 ao2_ref(mem, -1); 04315 } 04316 } 04317 ao2_unlock(q); 04318 queue_unref(q); 04319 } 04320 04321 if (foundinterface) { 04322 return RESULT_SUCCESS; 04323 } else if (!foundqueue) { 04324 ast_log (LOG_ERROR, "Invalid queuename\n"); 04325 } else { 04326 ast_log (LOG_ERROR, "Invalid interface\n"); 04327 } 04328 04329 return RESULT_FAILURE; 04330 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 554 of file app_queue.c.
References ARRAY_LEN, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
00555 { 00556 int i; 00557 00558 for (i = 0; i < ARRAY_LEN(queue_results); i++) { 00559 if (queue_results[i].id == res) { 00560 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 00561 return; 00562 } 00563 } 00564 }
static void set_queue_variables | ( | struct call_queue * | q, | |
struct ast_channel * | chan | |||
) | [static] |
Set variables of queue.
Definition at line 616 of file app_queue.c.
References 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, and call_queue::strategy.
Referenced by end_bridge_callback(), record_abandoned(), and try_calling().
00617 { 00618 char interfacevar[256]=""; 00619 float sl = 0; 00620 00621 if (q->setqueuevar) { 00622 sl = 0; 00623 if (q->callscompleted > 0) 00624 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 00625 00626 snprintf(interfacevar, sizeof(interfacevar), 00627 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 00628 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 00629 00630 pbx_builtin_setvar_multiple(chan, interfacevar); 00631 } 00632 }
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 3225 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.
Referenced by try_calling().
03226 { 03227 struct ast_datastore *ds; 03228 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 03229 03230 if (!qtds) { 03231 ast_log(LOG_WARNING, "Memory allocation error!\n"); 03232 return NULL; 03233 } 03234 03235 ast_channel_lock(qe->chan); 03236 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) { 03237 ast_channel_unlock(qe->chan); 03238 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 03239 return NULL; 03240 } 03241 03242 qtds->qe = qe; 03243 /* This member is refcounted in try_calling, so no need to add it here, too */ 03244 qtds->member = member; 03245 qtds->starttime = starttime; 03246 qtds->callcompletedinsl = callcompletedinsl; 03247 ds->data = qtds; 03248 ast_channel_datastore_add(qe->chan, ds); 03249 ast_channel_unlock(qe->chan); 03250 return ds; 03251 }
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 2441 of file app_queue.c.
References ast_debug, find_best(), callattempt::interface, queue_ent::linpos, queue_ent::linwrapped, and callattempt::metric.
Referenced by try_calling().
02442 { 02443 struct callattempt *best = find_best(outgoing); 02444 02445 if (best) { 02446 /* Ring just the best channel */ 02447 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 02448 qe->linpos = best->metric % 1000; 02449 } else { 02450 /* Just increment rrpos */ 02451 if (qe->linwrapped) { 02452 /* No more channels, start over */ 02453 qe->linpos = 0; 02454 } else { 02455 /* Prioritize next entry */ 02456 qe->linpos++; 02457 } 02458 } 02459 qe->linwrapped = 0; 02460 02461 return 0; 02462 }
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 2417 of file app_queue.c.
References ast_debug, find_best(), callattempt::interface, callattempt::metric, queue_ent::parent, call_queue::rrpos, and call_queue::wrapped.
Referenced by try_calling().
02418 { 02419 struct callattempt *best = find_best(outgoing); 02420 02421 if (best) { 02422 /* Ring just the best channel */ 02423 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 02424 qe->parent->rrpos = best->metric % 1000; 02425 } else { 02426 /* Just increment rrpos */ 02427 if (qe->parent->wrapped) { 02428 /* No more channels, start over */ 02429 qe->parent->rrpos = 0; 02430 } else { 02431 /* Prioritize next entry */ 02432 qe->parent->rrpos++; 02433 } 02434 } 02435 qe->parent->wrapped = 0; 02436 02437 return 0; 02438 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 578 of file app_queue.c.
References ARRAY_LEN, strategies, and strategy::strategy.
Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_queues().
00579 { 00580 int x; 00581 00582 for (x = 0; x < ARRAY_LEN(strategies); x++) { 00583 if (!strcasecmp(strategy, strategies[x].name)) 00584 return strategies[x].strategy; 00585 } 00586 00587 return -1; 00588 }
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 3307 of file app_queue.c.
References ast_channel::_state, AGENT, queue_ent::announce, ao2_alloc, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), asprintf, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CDR_FLAG_LOCKED, ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_check_hangup(), ast_clear_flag, ast_copy_string(), ast_datastore_alloc(), ast_datastore_free(), ast_debug, AST_DIGIT_ANY, AST_FEATURE_AUTOMIXMON, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_NO_H_EXTEN, AST_FEATURE_PARKCALL, AST_FEATURE_REDIRECT, ast_free, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_pbx_run_args(), ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), CALLER, member::calls, ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, member::dynamic, end_bridge_callback(), end_bridge_callback_data_fixup(), errno, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, member::interface, ast_channel::language, member::lastcall, member::lastqueue, leave_queue(), ast_dialed_interface::list, LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event, callattempt::member, call_queue::membercount, call_queue::memberdelay, call_queue::membergosub, call_queue::membermacro, member::membername, call_queue::members, call_queue::monfmt, call_queue::montype, call_queue::name, ast_channel::name, queue_ent::opos, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_multiple(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), member::penalty, queue_ent::pending, play_file(), queue_ent::pos, ast_channel::priority, QUEUE_EVENT_VARIABLES, queue_ref(), QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRMEMORY, queue_transfer_info, queues, member::realtime, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), send_agent_complete(), call_queue::servicelevel, set_queue_variables(), call_queue::setinterfacevar, call_queue::setqueueentryvar, setup_transfer_datastore(), call_queue::sound_callerannounce, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, queue_ent::start, member::status, store_next_lin(), store_next_rr(), call_queue::strategy, ast_channel::tech, call_queue::timeout, TIMEOUT_PRIORITY_APP, call_queue::timeoutpriority, ast_channel_tech::type, ast_cdr::uniqueid, ast_channel::uniqueid, update_queue(), vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.
Referenced by queue_exec().
03308 { 03309 struct member *cur; 03310 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 03311 int to, orig; 03312 char oldexten[AST_MAX_EXTENSION]=""; 03313 char oldcontext[AST_MAX_CONTEXT]=""; 03314 char queuename[256]=""; 03315 char interfacevar[256]=""; 03316 struct ast_channel *peer; 03317 struct ast_channel *which; 03318 struct callattempt *lpeer; 03319 struct member *member; 03320 struct ast_app *application; 03321 int res = 0, bridge = 0; 03322 int numbusies = 0; 03323 int x=0; 03324 char *announce = NULL; 03325 char digit = 0; 03326 time_t callstart; 03327 time_t now = time(NULL); 03328 struct ast_bridge_config bridge_config; 03329 char nondataquality = 1; 03330 char *agiexec = NULL; 03331 char *macroexec = NULL; 03332 char *gosubexec = NULL; 03333 int ret = 0; 03334 const char *monitorfilename; 03335 const char *monitor_exec; 03336 const char *monitor_options; 03337 char tmpid[256], tmpid2[256]; 03338 char meid[1024], meid2[1024]; 03339 char mixmonargs[1512]; 03340 struct ast_app *mixmonapp = NULL; 03341 char *p; 03342 char vars[2048]; 03343 int forwardsallowed = 1; 03344 int callcompletedinsl; 03345 struct ao2_iterator memi; 03346 struct ast_datastore *datastore, *transfer_ds; 03347 struct queue_end_bridge *queue_end_bridge = NULL; 03348 03349 ast_channel_lock(qe->chan); 03350 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 03351 ast_channel_unlock(qe->chan); 03352 03353 memset(&bridge_config, 0, sizeof(bridge_config)); 03354 tmpid[0] = 0; 03355 meid[0] = 0; 03356 time(&now); 03357 03358 /* If we've already exceeded our timeout, then just stop 03359 * This should be extremely rare. queue_exec will take care 03360 * of removing the caller and reporting the timeout as the reason. 03361 */ 03362 if (qe->expire && now >= qe->expire) { 03363 res = 0; 03364 goto out; 03365 } 03366 03367 for (; options && *options; options++) 03368 switch (*options) { 03369 case 't': 03370 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 03371 break; 03372 case 'T': 03373 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 03374 break; 03375 case 'w': 03376 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 03377 break; 03378 case 'W': 03379 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 03380 break; 03381 case 'c': 03382 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN); 03383 break; 03384 case 'd': 03385 nondataquality = 0; 03386 break; 03387 case 'h': 03388 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 03389 break; 03390 case 'H': 03391 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 03392 break; 03393 case 'k': 03394 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL); 03395 break; 03396 case 'K': 03397 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL); 03398 break; 03399 case 'n': 03400 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR) 03401 (*tries)++; 03402 else 03403 *tries = qe->parent->membercount; 03404 *noption = 1; 03405 break; 03406 case 'i': 03407 forwardsallowed = 0; 03408 break; 03409 case 'x': 03410 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); 03411 break; 03412 case 'X': 03413 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON); 03414 break; 03415 03416 } 03417 03418 /* Hold the lock while we setup the outgoing calls */ 03419 if (use_weight) 03420 ao2_lock(queues); 03421 ao2_lock(qe->parent); 03422 ast_debug(1, "%s is trying to call a queue member.\n", 03423 qe->chan->name); 03424 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 03425 if (!ast_strlen_zero(qe->announce)) 03426 announce = qe->announce; 03427 if (!ast_strlen_zero(announceoverride)) 03428 announce = announceoverride; 03429 03430 memi = ao2_iterator_init(qe->parent->members, 0); 03431 while ((cur = ao2_iterator_next(&memi))) { 03432 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 03433 struct ast_dialed_interface *di; 03434 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 03435 if (!tmp) { 03436 ao2_ref(cur, -1); 03437 ao2_unlock(qe->parent); 03438 if (use_weight) 03439 ao2_unlock(queues); 03440 goto out; 03441 } 03442 if (!datastore) { 03443 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { 03444 ao2_ref(cur, -1); 03445 ao2_unlock(qe->parent); 03446 if (use_weight) 03447 ao2_unlock(queues); 03448 free(tmp); 03449 goto out; 03450 } 03451 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 03452 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 03453 ao2_ref(cur, -1); 03454 ao2_unlock(&qe->parent); 03455 if (use_weight) 03456 ao2_unlock(queues); 03457 free(tmp); 03458 goto out; 03459 } 03460 datastore->data = dialed_interfaces; 03461 AST_LIST_HEAD_INIT(dialed_interfaces); 03462 03463 ast_channel_lock(qe->chan); 03464 ast_channel_datastore_add(qe->chan, datastore); 03465 ast_channel_unlock(qe->chan); 03466 } else 03467 dialed_interfaces = datastore->data; 03468 03469 AST_LIST_LOCK(dialed_interfaces); 03470 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 03471 if (!strcasecmp(cur->interface, di->interface)) { 03472 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 03473 di->interface); 03474 break; 03475 } 03476 } 03477 AST_LIST_UNLOCK(dialed_interfaces); 03478 03479 if (di) { 03480 free(tmp); 03481 continue; 03482 } 03483 03484 /* It is always ok to dial a Local interface. We only keep track of 03485 * which "real" interfaces have been dialed. The Local channel will 03486 * inherit this list so that if it ends up dialing a real interface, 03487 * it won't call one that has already been called. */ 03488 if (strncasecmp(cur->interface, "Local/", 6)) { 03489 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 03490 ao2_ref(cur, -1); 03491 ao2_unlock(qe->parent); 03492 if (use_weight) 03493 ao2_unlock(queues); 03494 free(tmp); 03495 goto out; 03496 } 03497 strcpy(di->interface, cur->interface); 03498 03499 AST_LIST_LOCK(dialed_interfaces); 03500 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 03501 AST_LIST_UNLOCK(dialed_interfaces); 03502 } 03503 03504 tmp->stillgoing = -1; 03505 tmp->member = cur; 03506 tmp->oldstatus = cur->status; 03507 tmp->lastcall = cur->lastcall; 03508 tmp->lastqueue = cur->lastqueue; 03509 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 03510 /* Special case: If we ring everyone, go ahead and ring them, otherwise 03511 just calculate their metric for the appropriate strategy */ 03512 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 03513 /* Put them in the list of outgoing thingies... We're ready now. 03514 XXX If we're forcibly removed, these outgoing calls won't get 03515 hung up XXX */ 03516 tmp->q_next = outgoing; 03517 outgoing = tmp; 03518 /* If this line is up, don't try anybody else */ 03519 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 03520 break; 03521 } else { 03522 ao2_ref(cur, -1); 03523 ast_free(tmp); 03524 } 03525 } 03526 03527 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) { 03528 /* Application arguments have higher timeout priority (behaviour for <=1.6) */ 03529 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 03530 to = (qe->expire - now) * 1000; 03531 else 03532 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 03533 } else { 03534 /* Config timeout is higher priority thatn application timeout */ 03535 if (qe->expire && qe->expire<=now) { 03536 to = 0; 03537 } else if (qe->parent->timeout) { 03538 to = qe->parent->timeout * 1000; 03539 } else { 03540 to = -1; 03541 } 03542 } 03543 orig = to; 03544 ++qe->pending; 03545 ao2_unlock(qe->parent); 03546 ring_one(qe, outgoing, &numbusies); 03547 if (use_weight) 03548 ao2_unlock(queues); 03549 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); 03550 /* The ast_channel_datastore_remove() function could fail here if the 03551 * datastore was moved to another channel during a masquerade. If this is 03552 * the case, don't free the datastore here because later, when the channel 03553 * to which the datastore was moved hangs up, it will attempt to free this 03554 * datastore again, causing a crash 03555 */ 03556 ast_channel_lock(qe->chan); 03557 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 03558 ast_datastore_free(datastore); 03559 } 03560 ast_channel_unlock(qe->chan); 03561 ao2_lock(qe->parent); 03562 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { 03563 store_next_rr(qe, outgoing); 03564 } 03565 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) { 03566 store_next_lin(qe, outgoing); 03567 } 03568 ao2_unlock(qe->parent); 03569 peer = lpeer ? lpeer->chan : NULL; 03570 if (!peer) { 03571 qe->pending = 0; 03572 if (to) { 03573 /* Must gotten hung up */ 03574 res = -1; 03575 } else { 03576 /* User exited by pressing a digit */ 03577 res = digit; 03578 } 03579 if (res == -1) 03580 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name); 03581 } else { /* peer is valid */ 03582 /* Ah ha! Someone answered within the desired timeframe. Of course after this 03583 we will always return with -1 so that it is hung up properly after the 03584 conversation. */ 03585 if (!strcmp(qe->chan->tech->type, "DAHDI")) 03586 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 03587 if (!strcmp(peer->tech->type, "DAHDI")) 03588 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 03589 /* Update parameters for the queue */ 03590 time(&now); 03591 recalc_holdtime(qe, (now - qe->start)); 03592 ao2_lock(qe->parent); 03593 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 03594 ao2_unlock(qe->parent); 03595 member = lpeer->member; 03596 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 03597 ao2_ref(member, 1); 03598 hangupcalls(outgoing, peer); 03599 outgoing = NULL; 03600 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 03601 int res2; 03602 03603 res2 = ast_autoservice_start(qe->chan); 03604 if (!res2) { 03605 if (qe->parent->memberdelay) { 03606 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 03607 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 03608 } 03609 if (!res2 && announce) { 03610 play_file(peer, announce); 03611 } 03612 if (!res2 && qe->parent->reportholdtime) { 03613 if (!play_file(peer, qe->parent->sound_reporthold)) { 03614 int holdtime, holdtimesecs; 03615 03616 time(&now); 03617 holdtime = abs((now - qe->start) / 60); 03618 holdtimesecs = abs((now - qe->start)); 03619 if (holdtime == 1) { 03620 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 03621 play_file(peer, qe->parent->sound_minute); 03622 } else { 03623 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 03624 play_file(peer, qe->parent->sound_minutes); 03625 } 03626 if (holdtimesecs > 1) { 03627 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL); 03628 play_file(peer, qe->parent->sound_seconds); 03629 } 03630 } 03631 } 03632 } 03633 res2 |= ast_autoservice_stop(qe->chan); 03634 if (ast_check_hangup(peer)) { 03635 /* Agent must have hung up */ 03636 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 03637 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 03638 if (qe->parent->eventwhencalled) 03639 manager_event(EVENT_FLAG_AGENT, "AgentDump", 03640 "Queue: %s\r\n" 03641 "Uniqueid: %s\r\n" 03642 "Channel: %s\r\n" 03643 "Member: %s\r\n" 03644 "MemberName: %s\r\n" 03645 "%s", 03646 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03647 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03648 ast_hangup(peer); 03649 ao2_ref(member, -1); 03650 goto out; 03651 } else if (res2) { 03652 /* Caller must have hung up just before being connected*/ 03653 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 03654 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 03655 record_abandoned(qe); 03656 ast_hangup(peer); 03657 ao2_ref(member, -1); 03658 return -1; 03659 } 03660 } 03661 /* Stop music on hold */ 03662 if (ringing) 03663 ast_indicate(qe->chan,-1); 03664 else 03665 ast_moh_stop(qe->chan); 03666 /* If appropriate, log that we have a destination channel */ 03667 if (qe->chan->cdr) 03668 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 03669 /* Make sure channels are compatible */ 03670 res = ast_channel_make_compatible(qe->chan, peer); 03671 if (res < 0) { 03672 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 03673 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 03674 record_abandoned(qe); 03675 ast_hangup(peer); 03676 ao2_ref(member, -1); 03677 return -1; 03678 } 03679 03680 /* Play announcement to the caller telling it's his turn if defined */ 03681 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) { 03682 if (play_file(qe->chan, qe->parent->sound_callerannounce)) 03683 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce); 03684 } 03685 03686 ao2_lock(qe->parent); 03687 /* if setinterfacevar is defined, make member variables available to the channel */ 03688 /* use pbx_builtin_setvar to set a load of variables with one call */ 03689 if (qe->parent->setinterfacevar) { 03690 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d", 03691 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime); 03692 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 03693 pbx_builtin_setvar_multiple(peer, interfacevar); 03694 } 03695 03696 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */ 03697 /* use pbx_builtin_setvar to set a load of variables with one call */ 03698 if (qe->parent->setqueueentryvar) { 03699 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d", 03700 (long) time(NULL) - qe->start, qe->opos); 03701 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 03702 pbx_builtin_setvar_multiple(peer, interfacevar); 03703 } 03704 03705 /* try to set queue variables if configured to do so*/ 03706 set_queue_variables(qe->parent, qe->chan); 03707 set_queue_variables(qe->parent, peer); 03708 ao2_unlock(qe->parent); 03709 03710 ast_channel_lock(qe->chan); 03711 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) { 03712 monitorfilename = ast_strdupa(monitorfilename); 03713 } 03714 ast_channel_unlock(qe->chan); 03715 /* Begin Monitoring */ 03716 if (qe->parent->monfmt && *qe->parent->monfmt) { 03717 if (!qe->parent->montype) { 03718 const char *monexec, *monargs; 03719 ast_debug(1, "Starting Monitor as requested.\n"); 03720 ast_channel_lock(qe->chan); 03721 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) { 03722 which = qe->chan; 03723 monexec = monexec ? ast_strdupa(monexec) : NULL; 03724 } 03725 else 03726 which = peer; 03727 ast_channel_unlock(qe->chan); 03728 if (monitorfilename) 03729 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); 03730 else if (qe->chan->cdr) 03731 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); 03732 else { 03733 /* Last ditch effort -- no CDR, make up something */ 03734 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03735 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); 03736 } 03737 if (!ast_strlen_zero(monexec)) { 03738 ast_monitor_setjoinfiles(which, 1); 03739 } 03740 } else { 03741 mixmonapp = pbx_findapp("MixMonitor"); 03742 03743 if (mixmonapp) { 03744 ast_debug(1, "Starting MixMonitor as requested.\n"); 03745 if (!monitorfilename) { 03746 if (qe->chan->cdr) 03747 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)); 03748 else 03749 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03750 } else { 03751 const char *m = monitorfilename; 03752 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) { 03753 switch (*m) { 03754 case '^': 03755 if (*(m + 1) == '{') 03756 *p = '$'; 03757 break; 03758 case ',': 03759 *p++ = '\\'; 03760 /* Fall through */ 03761 default: 03762 *p = *m; 03763 } 03764 if (*m == '\0') 03765 break; 03766 } 03767 if (p == tmpid2 + sizeof(tmpid2)) 03768 tmpid2[sizeof(tmpid2) - 1] = '\0'; 03769 03770 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 03771 } 03772 03773 ast_channel_lock(qe->chan); 03774 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) { 03775 monitor_exec = ast_strdupa(monitor_exec); 03776 } 03777 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) { 03778 monitor_options = ast_strdupa(monitor_options); 03779 } else { 03780 monitor_options = ""; 03781 } 03782 ast_channel_unlock(qe->chan); 03783 03784 if (monitor_exec) { 03785 const char *m = monitor_exec; 03786 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) { 03787 switch (*m) { 03788 case '^': 03789 if (*(m + 1) == '{') 03790 *p = '$'; 03791 break; 03792 case ',': 03793 *p++ = '\\'; 03794 /* Fall through */ 03795 default: 03796 *p = *m; 03797 } 03798 if (*m == '\0') 03799 break; 03800 } 03801 if (p == meid2 + sizeof(meid2)) 03802 meid2[sizeof(meid2) - 1] = '\0'; 03803 03804 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 03805 } 03806 03807 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt); 03808 03809 if (!ast_strlen_zero(monitor_exec)) 03810 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec); 03811 else 03812 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options); 03813 03814 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 03815 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 03816 if (qe->chan->cdr) 03817 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03818 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 03819 if (qe->chan->cdr) 03820 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03821 03822 } else { 03823 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 03824 } 03825 } 03826 } 03827 /* Drop out of the queue at this point, to prepare for next caller */ 03828 leave_queue(qe); 03829 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 03830 ast_debug(1, "app_queue: sendurl=%s.\n", url); 03831 ast_channel_sendurl(peer, url); 03832 } 03833 03834 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */ 03835 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */ 03836 if (!ast_strlen_zero(macro)) { 03837 macroexec = ast_strdupa(macro); 03838 } else { 03839 if (qe->parent->membermacro) 03840 macroexec = ast_strdupa(qe->parent->membermacro); 03841 } 03842 03843 if (!ast_strlen_zero(macroexec)) { 03844 ast_debug(1, "app_queue: macro=%s.\n", macroexec); 03845 03846 res = ast_autoservice_start(qe->chan); 03847 if (res) { 03848 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 03849 res = -1; 03850 } 03851 03852 application = pbx_findapp("Macro"); 03853 03854 if (application) { 03855 res = pbx_exec(peer, application, macroexec); 03856 ast_debug(1, "Macro exited with status %d\n", res); 03857 res = 0; 03858 } else { 03859 ast_log(LOG_ERROR, "Could not find application Macro\n"); 03860 res = -1; 03861 } 03862 03863 if (ast_autoservice_stop(qe->chan) < 0) { 03864 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 03865 res = -1; 03866 } 03867 } 03868 03869 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */ 03870 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */ 03871 if (!ast_strlen_zero(gosub)) { 03872 gosubexec = ast_strdupa(gosub); 03873 } else { 03874 if (qe->parent->membergosub) 03875 gosubexec = ast_strdupa(qe->parent->membergosub); 03876 } 03877 03878 if (!ast_strlen_zero(gosubexec)) { 03879 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec); 03880 03881 res = ast_autoservice_start(qe->chan); 03882 if (res) { 03883 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 03884 res = -1; 03885 } 03886 03887 application = pbx_findapp("Gosub"); 03888 03889 if (application) { 03890 char *gosub_args, *gosub_argstart; 03891 03892 /* Set where we came from */ 03893 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context)); 03894 ast_copy_string(peer->exten, "s", sizeof(peer->exten)); 03895 peer->priority = 0; 03896 03897 gosub_argstart = strchr(gosubexec, ','); 03898 if (gosub_argstart) { 03899 *gosub_argstart = 0; 03900 if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) { 03901 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 03902 gosub_args = NULL; 03903 } 03904 *gosub_argstart = ','; 03905 } else { 03906 if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) { 03907 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 03908 gosub_args = NULL; 03909 } 03910 } 03911 if (gosub_args) { 03912 res = pbx_exec(peer, application, gosub_args); 03913 if (!res) { 03914 struct ast_pbx_args args; 03915 memset(&args, 0, sizeof(args)); 03916 args.no_hangup_chan = 1; 03917 ast_pbx_run_args(peer, &args); 03918 } 03919 ast_free(gosub_args); 03920 ast_debug(1, "Gosub exited with status %d\n", res); 03921 } else { 03922 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); 03923 } 03924 } else { 03925 ast_log(LOG_ERROR, "Could not find application Gosub\n"); 03926 res = -1; 03927 } 03928 03929 if (ast_autoservice_stop(qe->chan) < 0) { 03930 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 03931 res = -1; 03932 } 03933 } 03934 03935 if (!ast_strlen_zero(agi)) { 03936 ast_debug(1, "app_queue: agi=%s.\n", agi); 03937 application = pbx_findapp("agi"); 03938 if (application) { 03939 agiexec = ast_strdupa(agi); 03940 ret = pbx_exec(qe->chan, application, agiexec); 03941 } else 03942 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 03943 } 03944 qe->handled++; 03945 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid, 03946 (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); 03947 if (update_cdr && qe->chan->cdr) 03948 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel)); 03949 if (qe->parent->eventwhencalled) 03950 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 03951 "Queue: %s\r\n" 03952 "Uniqueid: %s\r\n" 03953 "Channel: %s\r\n" 03954 "Member: %s\r\n" 03955 "MemberName: %s\r\n" 03956 "Holdtime: %ld\r\n" 03957 "BridgedChannel: %s\r\n" 03958 "Ringtime: %ld\r\n" 03959 "%s", 03960 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03961 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0), 03962 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03963 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 03964 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 03965 03966 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { 03967 queue_end_bridge->q = qe->parent; 03968 queue_end_bridge->chan = qe->chan; 03969 bridge_config.end_bridge_callback = end_bridge_callback; 03970 bridge_config.end_bridge_callback_data = queue_end_bridge; 03971 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; 03972 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need 03973 * to make sure to increase the refcount of this queue so it cannot be freed until we 03974 * are done with it. We remove this reference in end_bridge_callback. 03975 */ 03976 queue_ref(qe->parent); 03977 } 03978 03979 time(&callstart); 03980 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 03981 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 03982 03983 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log 03984 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary 03985 */ 03986 ast_channel_lock(qe->chan); 03987 if (!attended_transfer_occurred(qe->chan)) { 03988 struct ast_datastore *tds; 03989 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) { 03990 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 03991 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 03992 (long) (time(NULL) - callstart), qe->opos); 03993 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 03994 } else if (ast_check_hangup(qe->chan)) { 03995 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 03996 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03997 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER); 03998 } else { 03999 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 04000 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 04001 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT); 04002 } 04003 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 04004 ast_channel_datastore_remove(qe->chan, tds); 04005 } 04006 update_queue(qe->parent, member, callcompletedinsl); 04007 } 04008 04009 if (transfer_ds) { 04010 ast_datastore_free(transfer_ds); 04011 } 04012 ast_channel_unlock(qe->chan); 04013 ast_hangup(peer); 04014 res = bridge ? bridge : 1; 04015 ao2_ref(member, -1); 04016 } 04017 out: 04018 hangupcalls(outgoing, NULL); 04019 04020 return res; 04021 }
static int unload_module | ( | void | ) | [static] |
Definition at line 6620 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, ast_cli_unregister_multiple(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_event_unsubscribe(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state_sub, devicestate_tps, queue_unref(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, and queuewaitingcount_function.
06621 { 06622 int res; 06623 struct ast_context *con; 06624 struct ao2_iterator q_iter; 06625 struct call_queue *q = NULL; 06626 06627 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 06628 res = ast_manager_unregister("QueueStatus"); 06629 res |= ast_manager_unregister("Queues"); 06630 res |= ast_manager_unregister("QueueRule"); 06631 res |= ast_manager_unregister("QueueSummary"); 06632 res |= ast_manager_unregister("QueueAdd"); 06633 res |= ast_manager_unregister("QueueRemove"); 06634 res |= ast_manager_unregister("QueuePause"); 06635 res |= ast_manager_unregister("QueueLog"); 06636 res |= ast_manager_unregister("QueuePenalty"); 06637 res |= ast_unregister_application(app_aqm); 06638 res |= ast_unregister_application(app_rqm); 06639 res |= ast_unregister_application(app_pqm); 06640 res |= ast_unregister_application(app_upqm); 06641 res |= ast_unregister_application(app_ql); 06642 res |= ast_unregister_application(app); 06643 res |= ast_custom_function_unregister(&queuevar_function); 06644 res |= ast_custom_function_unregister(&queuemembercount_function); 06645 res |= ast_custom_function_unregister(&queuemembercount_dep); 06646 res |= ast_custom_function_unregister(&queuememberlist_function); 06647 res |= ast_custom_function_unregister(&queuewaitingcount_function); 06648 res |= ast_custom_function_unregister(&queuememberpenalty_function); 06649 06650 if (device_state_sub) 06651 ast_event_unsubscribe(device_state_sub); 06652 06653 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) { 06654 ast_context_remove_extension2(con, "s", 1, NULL, 0); 06655 ast_context_destroy(con, "app_queue"); /* leave no trace */ 06656 } 06657 06658 clear_and_free_interfaces(); 06659 06660 q_iter = ao2_iterator_init(queues, 0); 06661 while ((q = ao2_iterator_next(&q_iter))) { 06662 ao2_unlink(queues, q); 06663 queue_unref(q); 06664 } 06665 ao2_ref(queues, -1); 06666 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps); 06667 ast_unload_realtime("queue_members"); 06668 return res; 06669 }
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 2874 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 join_queue(), queue_exec(), and wait_our_turn().
02875 { 02876 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value; 02877 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value; 02878 char max_penalty_str[20], min_penalty_str[20]; 02879 /* a relative change to the penalty could put it below 0 */ 02880 if (max_penalty < 0) 02881 max_penalty = 0; 02882 if (min_penalty < 0) 02883 min_penalty = 0; 02884 if (min_penalty > max_penalty) 02885 min_penalty = max_penalty; 02886 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty); 02887 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty); 02888 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str); 02889 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str); 02890 qe->max_penalty = max_penalty; 02891 qe->min_penalty = min_penalty; 02892 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); 02893 qe->pr = AST_LIST_NEXT(qe->pr, list); 02894 }
static int update_queue | ( | struct call_queue * | q, | |
struct member * | member, | |||
int | callcompletedinsl | |||
) | [static] |
update the queue status
Always | 0 |
Definition at line 3015 of file app_queue.c.
References ao2_find, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, OBJ_POINTER, and queues.
Referenced by queue_transfer_fixup(), and try_calling().
03016 { 03017 struct member *mem; 03018 struct call_queue *qtmp; 03019 struct ao2_iterator queue_iter; 03020 03021 if (shared_lastcall) { 03022 queue_iter = ao2_iterator_init(queues, 0); 03023 while ((qtmp = ao2_iterator_next(&queue_iter))) { 03024 ao2_lock(qtmp); 03025 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { 03026 time(&mem->lastcall); 03027 mem->calls++; 03028 mem->lastqueue = q; 03029 ao2_ref(mem, -1); 03030 } 03031 ao2_unlock(qtmp); 03032 ao2_ref(qtmp, -1); 03033 } 03034 } else { 03035 ao2_lock(q); 03036 time(&member->lastcall); 03037 member->calls++; 03038 member->lastqueue = q; 03039 ao2_unlock(q); 03040 } 03041 ao2_lock(q); 03042 q->callscompleted++; 03043 if (callcompletedinsl) 03044 q->callscompletedinsl++; 03045 ao2_unlock(q); 03046 return 0; 03047 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 1638 of file app_queue.c.
References ast_strlen_zero(), ast_update_realtime(), member::rt_uniqueid, and SENTINEL.
Referenced by set_member_paused().
01639 { 01640 int ret = -1; 01641 01642 if (ast_strlen_zero(mem->rt_uniqueid)) 01643 return ret; 01644 01645 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) 01646 ret = 0; 01647 01648 return ret; 01649 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 1652 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, queues, member::realtime, remove_from_interfaces(), rt_handle_member_record(), S_OR, SENTINEL, and member::state_interface.
Referenced by load_realtime_queue(), and queue_exec().
01653 { 01654 struct ast_config *member_config = NULL; 01655 struct member *m; 01656 char *interface = NULL; 01657 struct ao2_iterator mem_iter; 01658 01659 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) { 01660 /*This queue doesn't have realtime members*/ 01661 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name); 01662 return; 01663 } 01664 01665 ao2_lock(queues); 01666 ao2_lock(q); 01667 01668 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 01669 mem_iter = ao2_iterator_init(q->members, 0); 01670 while ((m = ao2_iterator_next(&mem_iter))) { 01671 if (m->realtime) 01672 m->dead = 1; 01673 ao2_ref(m, -1); 01674 } 01675 01676 while ((interface = ast_category_browse(member_config, interface))) { 01677 rt_handle_member_record(q, interface, 01678 ast_variable_retrieve(member_config, interface, "uniqueid"), 01679 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 01680 ast_variable_retrieve(member_config, interface, "penalty"), 01681 ast_variable_retrieve(member_config, interface, "paused"), 01682 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface)); 01683 } 01684 01685 /* Delete all realtime members that have been deleted in DB. */ 01686 mem_iter = ao2_iterator_init(q->members, 0); 01687 while ((m = ao2_iterator_next(&mem_iter))) { 01688 if (m->dead) { 01689 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 01690 ao2_unlink(q->members, m); 01691 remove_from_interfaces(m->state_interface, 0); 01692 q->membercount--; 01693 } 01694 ao2_ref(m, -1); 01695 } 01696 ao2_unlock(q); 01697 ao2_unlock(queues); 01698 ast_config_destroy(member_config); 01699 }
static int update_status | ( | const char * | interface, | |
const int | status | |||
) | [static] |
set a member's status based on device state of that member's state_interface.
Lock interface list find sc, iterate through each queues queue_member list for member to update state inside queues
Definition at line 714 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_strdupa, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event, member::membername, member::paused, member::penalty, queue_unref(), queues, member::realtime, member::state_interface, and member::status.
Referenced by handle_statechange(), and ring_entry().
00715 { 00716 struct member *cur; 00717 struct ao2_iterator mem_iter, queue_iter; 00718 struct call_queue *q; 00719 00720 queue_iter = ao2_iterator_init(queues, 0); 00721 while ((q = ao2_iterator_next(&queue_iter))) { 00722 ao2_lock(q); 00723 mem_iter = ao2_iterator_init(q->members, 0); 00724 while ((cur = ao2_iterator_next(&mem_iter))) { 00725 char *tmp_interface; 00726 char *slash_pos; 00727 tmp_interface = ast_strdupa(cur->state_interface); 00728 if ((slash_pos = strchr(tmp_interface, '/'))) 00729 if (!strncasecmp(tmp_interface, "Local", 5) && (slash_pos = strchr(slash_pos + 1, '/'))) 00730 *slash_pos = '\0'; 00731 00732 if (strcasecmp(interface, tmp_interface)) { 00733 ao2_ref(cur, -1); 00734 continue; 00735 } 00736 00737 if (cur->status != status) { 00738 cur->status = status; 00739 if (q->maskmemberstatus) { 00740 ao2_ref(cur, -1); 00741 continue; 00742 } 00743 00744 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 00745 "Queue: %s\r\n" 00746 "Location: %s\r\n" 00747 "MemberName: %s\r\n" 00748 "Membership: %s\r\n" 00749 "Penalty: %d\r\n" 00750 "CallsTaken: %d\r\n" 00751 "LastCall: %d\r\n" 00752 "Status: %d\r\n" 00753 "Paused: %d\r\n", 00754 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", 00755 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); 00756 } 00757 ao2_ref(cur, -1); 00758 } 00759 queue_unref(q); 00760 ao2_unlock(q); 00761 } 00762 00763 return 0; 00764 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
UnPauseQueueMember application.
Definition at line 4500 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), chan, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
04501 { 04502 char *parse; 04503 AST_DECLARE_APP_ARGS(args, 04504 AST_APP_ARG(queuename); 04505 AST_APP_ARG(interface); 04506 AST_APP_ARG(options); 04507 AST_APP_ARG(reason); 04508 ); 04509 04510 if (ast_strlen_zero(data)) { 04511 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n"); 04512 return -1; 04513 } 04514 04515 parse = ast_strdupa(data); 04516 04517 AST_STANDARD_APP_ARGS(args, parse); 04518 04519 if (ast_strlen_zero(args.interface)) { 04520 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 04521 return -1; 04522 } 04523 04524 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) { 04525 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 04526 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 04527 return 0; 04528 } 04529 04530 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 04531 04532 return 0; 04533 }
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 1805 of file app_queue.c.
References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::context, queue_ent::digits, and queue_ent::valid_digits.
Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), and wait_our_turn().
01806 { 01807 int digitlen = strlen(qe->digits); 01808 01809 /* Prevent possible buffer overflow */ 01810 if (digitlen < sizeof(qe->digits) - 2) { 01811 qe->digits[digitlen] = digit; 01812 qe->digits[digitlen + 1] = '\0'; 01813 } else { 01814 qe->digits[0] = '\0'; 01815 return 0; 01816 } 01817 01818 /* If there's no context to goto, short-circuit */ 01819 if (ast_strlen_zero(qe->context)) 01820 return 0; 01821 01822 /* If the extension is bad, then reset the digits to blank */ 01823 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) { 01824 qe->digits[0] = '\0'; 01825 return 0; 01826 } 01827 01828 /* We have an exact match */ 01829 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 01830 qe->valid_digits = 1; 01831 /* Return 1 on a successful goto */ 01832 return 1; 01833 } 01834 01835 return 0; 01836 }
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 2167 of file app_queue.c.
References ast_copy_string(), ast_str_alloca, buf, queue_ent::chan, and pbx_builtin_serialize_variables().
Referenced by ring_entry(), send_agent_complete(), and try_calling().
02168 { 02169 struct ast_str *buf = ast_str_alloca(len + 1); 02170 char *tmp; 02171 02172 if (pbx_builtin_serialize_variables(chan, &buf)) { 02173 int i, j; 02174 02175 /* convert "\n" to "\nVariable: " */ 02176 strcpy(vars, "Variable: "); 02177 tmp = buf->str; 02178 02179 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 02180 vars[j] = tmp[i]; 02181 02182 if (tmp[i + 1] == '\0') 02183 break; 02184 if (tmp[i] == '\n') { 02185 vars[j++] = '\r'; 02186 vars[j++] = '\n'; 02187 02188 ast_copy_string(&(vars[j]), "Variable: ", len - j); 02189 j += 9; 02190 } 02191 } 02192 if (j > len - 3) 02193 j = len - 3; 02194 vars[j++] = '\r'; 02195 vars[j++] = '\n'; 02196 vars[j] = '\0'; 02197 } else { 02198 /* there are no channel variables; leave it blank */ 02199 *vars = '\0'; 02200 } 02201 return vars; 02202 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 4023 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
04024 { 04025 /* Don't need to hold the lock while we setup the outgoing calls */ 04026 int retrywait = qe->parent->retry * 1000; 04027 04028 int res = ast_waitfordigit(qe->chan, retrywait); 04029 if (res > 0 && !valid_exit(qe, res)) 04030 res = 0; 04031 04032 return res; 04033 }
static struct callattempt* wait_for_answer | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing, | |||
int * | to, | |||
char * | digit, | |||
int | prebusies, | |||
int | caller_disconnect, | |||
int | forwardsallowed | |||
) | [static] |
Wait for a member to answer the call.
[in] | qe | the queue_ent corresponding to the caller in the queue |
[in] | outgoing | the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero |
[in] | to | the amount of time (in milliseconds) to wait for a response |
[out] | digit | if a user presses a digit to exit the queue, this is the digit the caller pressed |
[in] | prebusies | number of busy members calculated prior to calling wait_for_answer |
[in] | caller_disconnect | if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call |
[in] | forwardsallowed | used to detect if we should allow call forwarding, based on the 'i' option to Queue() |
Definition at line 2574 of file app_queue.c.
References AST_MAX_WATCHERS, ast_poll_channel_add(), callattempt::call_next, callattempt::chan, queue_ent::chan, f, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, status, callattempt::stillgoing, and call_queue::strategy.
02575 { 02576 const char *queue = qe->parent->name; 02577 struct callattempt *o, *start = NULL, *prev = NULL; 02578 int status; 02579 int numbusies = prebusies; 02580 int numnochan = 0; 02581 int stillgoing = 0; 02582 int orig = *to; 02583 struct ast_frame *f; 02584 struct callattempt *peer = NULL; 02585 struct ast_channel *winner; 02586 struct ast_channel *in = qe->chan; 02587 char on[80] = ""; 02588 char membername[80] = ""; 02589 long starttime = 0; 02590 long endtime = 0; 02591 #ifdef HAVE_EPOLL 02592 struct callattempt *epollo; 02593 #endif 02594 02595 starttime = (long) time(NULL); 02596 #ifdef HAVE_EPOLL 02597 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 02598 if (epollo->chan) 02599 ast_poll_channel_add(in, epollo->chan); 02600 } 02601 #endif 02602 02603 while (*to && !peer) { 02604 int numlines, retry, pos = 1; 02605 struct ast_channel *watchers[AST_MAX_WATCHERS]; 02606 watchers[0] = in; 02607 start = NULL; 02608 02609 for (retry = 0; retry < 2; retry++) { 02610 numlines = 0; 02611 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 02612 if (o->stillgoing) { /* Keep track of important channels */ 02613 stillgoing = 1; 02614 if (o->chan) { 02615 watchers[pos++] = o->chan; 02616 if (!start) 02617 start = o; 02618 else 02619 prev->call_next = o; 02620 prev = o; 02621 } 02622 } 02623 numlines++; 02624 } 02625 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 02626 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 02627 break; 02628 /* On "ringall" strategy we only move to the next penalty level 02629 when *all* ringing phones are done in the current penalty level */ 02630 ring_one(qe, outgoing, &numbusies); 02631 /* and retry... */ 02632 } 02633 if (pos == 1 /* not found */) { 02634 if (numlines == (numbusies + numnochan)) { 02635 ast_debug(1, "Everyone is busy at this time\n"); 02636 } else { 02637 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan); 02638 } 02639 *to = 0; 02640 return NULL; 02641 } 02642 winner = ast_waitfor_n(watchers, pos, to); 02643 for (o = start; o; o = o->call_next) { 02644 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 02645 if (!peer) { 02646 ast_verb(3, "%s answered %s\n", o->chan->name, in->name); 02647 peer = o; 02648 } 02649 } else if (o->chan && (o->chan == winner)) { 02650 02651 ast_copy_string(on, o->member->interface, sizeof(on)); 02652 ast_copy_string(membername, o->member->membername, sizeof(membername)); 02653 02654 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 02655 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); 02656 numnochan++; 02657 do_hang(o); 02658 winner = NULL; 02659 continue; 02660 } else if (!ast_strlen_zero(o->chan->call_forward)) { 02661 char tmpchan[256]; 02662 char *stuff; 02663 char *tech; 02664 02665 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 02666 if ((stuff = strchr(tmpchan, '/'))) { 02667 *stuff++ = '\0'; 02668 tech = tmpchan; 02669 } else { 02670 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 02671 stuff = tmpchan; 02672 tech = "Local"; 02673 } 02674 /* Before processing channel, go ahead and check for forwarding */ 02675 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); 02676 /* Setup parameters */ 02677 o->chan = ast_request(tech, in->nativeformats, stuff, &status); 02678 if (!o->chan) { 02679 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); 02680 o->stillgoing = 0; 02681 numnochan++; 02682 } else { 02683 ast_channel_inherit_variables(in, o->chan); 02684 ast_channel_datastore_inherit(in, o->chan); 02685 if (o->chan->cid.cid_num) 02686 ast_free(o->chan->cid.cid_num); 02687 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); 02688 02689 if (o->chan->cid.cid_name) 02690 ast_free(o->chan->cid.cid_name); 02691 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); 02692 02693 ast_string_field_set(o->chan, accountcode, in->accountcode); 02694 o->chan->cdrflags = in->cdrflags; 02695 02696 if (in->cid.cid_ani) { 02697 if (o->chan->cid.cid_ani) 02698 ast_free(o->chan->cid.cid_ani); 02699 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); 02700 } 02701 if (o->chan->cid.cid_rdnis) 02702 ast_free(o->chan->cid.cid_rdnis); 02703 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); 02704 if (ast_call(o->chan, tmpchan, 0)) { 02705 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); 02706 do_hang(o); 02707 numnochan++; 02708 } 02709 } 02710 /* Hangup the original channel now, in case we needed it */ 02711 ast_hangup(winner); 02712 continue; 02713 } 02714 f = ast_read(winner); 02715 if (f) { 02716 if (f->frametype == AST_FRAME_CONTROL) { 02717 switch (f->subclass) { 02718 case AST_CONTROL_ANSWER: 02719 /* This is our guy if someone answered. */ 02720 if (!peer) { 02721 ast_verb(3, "%s answered %s\n", o->chan->name, in->name); 02722 peer = o; 02723 } 02724 break; 02725 case AST_CONTROL_BUSY: 02726 ast_verb(3, "%s is busy\n", o->chan->name); 02727 if (in->cdr) 02728 ast_cdr_busy(in->cdr); 02729 do_hang(o); 02730 endtime = (long) time(NULL); 02731 endtime -= starttime; 02732 rna(endtime * 1000, qe, on, membername, 0); 02733 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02734 if (qe->parent->timeoutrestart) 02735 *to = orig; 02736 ring_one(qe, outgoing, &numbusies); 02737 } 02738 numbusies++; 02739 break; 02740 case AST_CONTROL_CONGESTION: 02741 ast_verb(3, "%s is circuit-busy\n", o->chan->name); 02742 if (in->cdr) 02743 ast_cdr_busy(in->cdr); 02744 endtime = (long) time(NULL); 02745 endtime -= starttime; 02746 rna(endtime * 1000, qe, on, membername, 0); 02747 do_hang(o); 02748 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02749 if (qe->parent->timeoutrestart) 02750 *to = orig; 02751 ring_one(qe, outgoing, &numbusies); 02752 } 02753 numbusies++; 02754 break; 02755 case AST_CONTROL_RINGING: 02756 ast_verb(3, "%s is ringing\n", o->chan->name); 02757 break; 02758 case AST_CONTROL_OFFHOOK: 02759 /* Ignore going off hook */ 02760 break; 02761 default: 02762 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass); 02763 } 02764 } 02765 ast_frfree(f); 02766 } else { 02767 endtime = (long) time(NULL) - starttime; 02768 rna(endtime * 1000, qe, on, membername, 1); 02769 do_hang(o); 02770 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02771 if (qe->parent->timeoutrestart) 02772 *to = orig; 02773 ring_one(qe, outgoing, &numbusies); 02774 } 02775 } 02776 } 02777 } 02778 if (winner == in) { 02779 f = ast_read(in); 02780 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02781 /* Got hung up */ 02782 *to = -1; 02783 if (f) { 02784 if (f->data.uint32) { 02785 in->hangupcause = f->data.uint32; 02786 } 02787 ast_frfree(f); 02788 } 02789 return NULL; 02790 } 02791 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { 02792 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass); 02793 *to = 0; 02794 ast_frfree(f); 02795 return NULL; 02796 } 02797 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) { 02798 ast_verb(3, "User pressed digit: %c\n", f->subclass); 02799 *to = 0; 02800 *digit = f->subclass; 02801 ast_frfree(f); 02802 return NULL; 02803 } 02804 ast_frfree(f); 02805 } 02806 if (!*to) { 02807 for (o = start; o; o = o->call_next) 02808 rna(orig, qe, o->interface, o->member->membername, 1); 02809 } 02810 } 02811 02812 #ifdef HAVE_EPOLL 02813 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 02814 if (epollo->chan) 02815 ast_poll_channel_del(in, epollo->chan); 02816 } 02817 #endif 02818 02819 return peer; 02820 }
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 2906 of file app_queue.c.
References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, queue_ent::expire, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, queue_ent::min_penalty, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::pr, QUEUE_EMPTY_LOOSE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, penalty_rule::time, ast_channel::uniqueid, update_qe_rule(), and valid_exit().
Referenced by queue_exec().
02907 { 02908 int res = 0; 02909 02910 /* This is the holding pen for callers 2 through maxlen */ 02911 for (;;) { 02912 enum queue_member_status status = QUEUE_NORMAL; 02913 int exit = 0; 02914 02915 if (is_our_turn(qe)) 02916 break; 02917 02918 /* If we have timed out, break out */ 02919 if (qe->expire && (time(NULL) >= qe->expire)) { 02920 *reason = QUEUE_TIMEOUT; 02921 break; 02922 } 02923 02924 /* If we are going to exit due to a leavewhenempty condition, we should 02925 * actually attempt to keep the caller in the queue until we have 02926 * exhausted all penalty rules. 02927 */ 02928 for (; !exit || qe->pr; update_qe_rule(qe)) { 02929 status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty); 02930 02931 if (!qe->pr || status == QUEUE_NORMAL) { 02932 break; 02933 } 02934 02935 /* leave the queue if no agents, if enabled */ 02936 if ((qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) || 02937 ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) || 02938 ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS))) { 02939 continue; 02940 } else { 02941 exit = 1; 02942 } 02943 } 02944 02945 if (qe->parent->leavewhenempty && (status == QUEUE_NO_MEMBERS)) { 02946 *reason = QUEUE_LEAVEEMPTY; 02947 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 02948 leave_queue(qe); 02949 break; 02950 } 02951 02952 /* leave the queue if no reachable agents, if enabled */ 02953 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (status == QUEUE_NO_REACHABLE_MEMBERS || status == QUEUE_NO_UNPAUSED_REACHABLE_MEMBERS)) { 02954 *reason = QUEUE_LEAVEUNAVAIL; 02955 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 02956 leave_queue(qe); 02957 break; 02958 } 02959 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_LOOSE) && (status == QUEUE_NO_REACHABLE_MEMBERS)) { 02960 *reason = QUEUE_LEAVEUNAVAIL; 02961 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 02962 leave_queue(qe); 02963 break; 02964 } 02965 02966 /* Make a position announcement, if enabled */ 02967 if (qe->parent->announcefrequency && 02968 (res = say_position(qe,ringing))) 02969 break; 02970 02971 /* If we have timed out, break out */ 02972 if (qe->expire && (time(NULL) >= qe->expire)) { 02973 *reason = QUEUE_TIMEOUT; 02974 break; 02975 } 02976 02977 /* Make a periodic announcement, if enabled */ 02978 if (qe->parent->periodicannouncefrequency && 02979 (res = say_periodic_announcement(qe,ringing))) 02980 break; 02981 02982 /* see if we need to move to the next penalty level for this queue */ 02983 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) { 02984 update_qe_rule(qe); 02985 } 02986 02987 /* If we have timed out, break out */ 02988 if (qe->expire && (time(NULL) >= qe->expire)) { 02989 *reason = QUEUE_TIMEOUT; 02990 break; 02991 } 02992 02993 /* Wait a second before checking again */ 02994 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 02995 if (res > 0 && !valid_exit(qe, res)) 02996 res = 0; 02997 else 02998 break; 02999 } 03000 03001 /* If we have timed out, break out */ 03002 if (qe->expire && (time(NULL) >= qe->expire)) { 03003 *reason = QUEUE_TIMEOUT; 03004 break; 03005 } 03006 } 03007 03008 return res; 03009 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "a9c98e5d177805051735cb5b0b16b0a0" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 6737 of file app_queue.c.
char* app = "Queue" [static] |
Definition at line 152 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 211 of file app_queue.c.
char* app_aqm_descrip [static] |
Definition at line 213 of file app_queue.c.
char* app_aqm_synopsis = "Dynamically adds queue members" [static] |
Definition at line 212 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 237 of file app_queue.c.
char* app_pqm_descrip [static] |
Definition at line 239 of file app_queue.c.
char* app_pqm_synopsis = "Pauses a queue member" [static] |
Definition at line 238 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 270 of file app_queue.c.
char* app_ql_descrip [static] |
Initial value:
" QueueLog(queuename,uniqueid,agent,event[,additionalinfo]):\n" "Allows you to write your own events into the queue log\n" "Example: QueueLog(101,${UNIQUEID},${AGENT},WENTONBREAK,600)\n"
Definition at line 272 of file app_queue.c.
char* app_ql_synopsis = "Writes to the queue_log" [static] |
Definition at line 271 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 224 of file app_queue.c.
char* app_rqm_descrip [static] |
Definition at line 226 of file app_queue.c.
char* app_rqm_synopsis = "Dynamically removes queue members" [static] |
Definition at line 225 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 255 of file app_queue.c.
char* app_upqm_descrip [static] |
Definition at line 257 of file app_queue.c.
char* app_upqm_synopsis = "Unpauses a queue member" [static] |
Definition at line 256 of file app_queue.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 6737 of file app_queue.c.
int autofill_default = 0 [static] |
struct ast_cli_entry cli_queue[] [static] |
char* descrip [static] |
Definition at line 156 of file app_queue.c.
struct ast_event_sub* device_state_sub [static] |
struct ast_taskprocessor* devicestate_tps [static] |
Definition at line 136 of file app_queue.c.
Referenced by device_state_cb(), load_module(), and unload_module().
enum queue_result id |
Definition at line 318 of file app_queue.c.
Referenced by _sip_show_peers(), amixer_max(), idemodulator(), and setamixer().
int montype_default = 0 [static] |
const char* 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 6601 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 6607 of file app_queue.c.
int queue_keep_stats = 0 [static] |
int queue_persistent_members = 0 [static] |
struct { ... } queue_results[] |
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 3172 of file app_queue.c.
Referenced by attended_transfer_occurred(), queue_transfer_fixup(), setup_transfer_datastore(), and try_calling().
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] |
Definition at line 545 of file app_queue.c.
Referenced by __queues_show(), add_to_queue(), compare_weight(), complete_queue(), complete_queue_remove_member(), find_queue_by_name_rt(), get_member_penalty(), interface_exists_global(), join_queue(), leave_queue(), load_module(), load_realtime_queue(), manager_queues_status(), manager_queues_summary(), queue_function_queuememberlist(), queue_function_queuewaitingcount(), queue_function_var(), reload_queue_members(), reload_queues(), remove_from_queue(), set_member_paused(), set_member_penalty(), try_calling(), unload_module(), update_queue(), update_realtime_members(), and update_status().
struct ast_custom_function queuevar_function [static] |
struct ast_custom_function queuewaitingcount_function [static] |
const char qum_cmd_usage[] [static] |
Initial value:
"Usage: queue unpause member <channel> in <queue> reason <reason>\n"
Definition at line 6604 of file app_queue.c.
int shared_lastcall = 0 [static] |
struct strategy strategies[] [static] |
Referenced by int2strat(), and strat2int().
char* synopsis = "Queue a call for a call queue" [static] |
Definition at line 154 of file app_queue.c.
char* text |
Definition at line 319 of file app_queue.c.
Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), festival_exec(), handle_response(), iconv_read(), method_match(), parse_sip_options(), process_sdp(), reqprep(), sendtext_exec(), set_queue_result(), and sip_new().
int update_cdr = 0 [static] |
queues.conf [general] option
Definition at line 304 of file app_queue.c.
Referenced by login_exec().
int use_weight = 0 [static] |