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