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