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 | member |
| struct | penalty_rule |
| struct | queue_end_bridge |
| struct | queue_ent |
| struct | queue_transfer_ds |
| struct | rule_list |
| struct | rule_lists |
| struct | statechange |
| struct | strategy |
Defines | |
| #define | ANNOUNCEHOLDTIME_ALWAYS 1 |
| #define | ANNOUNCEHOLDTIME_ONCE 2 |
| #define | ANNOUNCEPOSITION_LIMIT 4 |
| #define | ANNOUNCEPOSITION_MORE_THAN 3 |
| #define | ANNOUNCEPOSITION_NO 2 |
| #define | ANNOUNCEPOSITION_YES 1 |
| #define | AST_MAX_WATCHERS 256 |
| #define | DEFAULT_MIN_ANNOUNCE_FREQUENCY 15 |
| #define | DEFAULT_RETRY 5 |
| #define | DEFAULT_TIMEOUT 15 |
| #define | MAX_PERIODIC_ANNOUNCEMENTS 10 |
| #define | MAX_QUEUE_BUCKETS 53 |
| #define | PM_MAX_LEN 8192 |
| #define | QUEUE_EVENT_VARIABLES 3 |
| #define | queue_t_ref(a, b) queue_ref(a) |
| #define | queue_t_unref(a, b) queue_unref(a) |
| #define | queues_t_link(c, q, tag) ao2_t_link(c,q,tag) |
| #define | queues_t_unlink(c, q, tag) ao2_t_unlink(c,q,tag) |
| #define | RECHECK 1 |
| #define | RES_EXISTS (-1) |
| #define | RES_NOSUCHQUEUE (-3) |
| #define | RES_NOT_DYNAMIC (-4) |
| #define | RES_OKAY 0 |
| #define | RES_OUTOFMEMORY (-2) |
Enumerations | |
| enum | { QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_WRANDOM } |
| enum | agent_complete_reason { CALLER, AGENT, TRANSFER } |
| enum | empty_conditions { QUEUE_EMPTY_PENALTY = (1 << 0), QUEUE_EMPTY_PAUSED = (1 << 1), QUEUE_EMPTY_INUSE = (1 << 2), QUEUE_EMPTY_RINGING = (1 << 3), QUEUE_EMPTY_UNAVAILABLE = (1 << 4), QUEUE_EMPTY_INVALID = (1 << 5), QUEUE_EMPTY_UNKNOWN = (1 << 6), QUEUE_EMPTY_WRAPUP = (1 << 7) } |
| enum | queue_reload_mask { QUEUE_RELOAD_PARAMETERS = (1 << 0), QUEUE_RELOAD_MEMBER = (1 << 1), QUEUE_RELOAD_RULES = (1 << 2), QUEUE_RESET_STATS = (1 << 3) } |
| enum | queue_result { QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3, QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7 } |
| enum | queue_timeout_priority { TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF } |
Functions | |
| static char * | __queues_show (struct mansession *s, int fd, int argc, char **argv) |
| Show queue(s) status and statistics. | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface) |
| Add member to queue. | |
| static struct call_queue * | alloc_queue (const char *queuename) |
| static int | aqm_exec (struct ast_channel *chan, 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_queue (struct call_queue *q) |
| static int | clear_stats (const char *queuename) |
| Facilitates resetting statistics for a queue. | |
| static int | compare_weight (struct call_queue *rq, struct member *member) |
| static char * | complete_queue (const char *line, const char *word, int pos, int state) |
| static char * | complete_queue_add_member (const char *line, const char *word, int pos, int state) |
| static char * | complete_queue_pause_member (const char *line, const char *word, int pos, int state) |
| static char * | complete_queue_remove_member (const char *line, const char *word, int pos, int state) |
| static char * | complete_queue_rule_show (const char *line, const char *word, int pos, int state) |
| static char * | complete_queue_set_member_penalty (const char *line, const char *word, int pos, int state) |
| static char * | complete_queue_show (const char *line, const char *word, int pos, int state) |
| static int | compress_char (const char c) |
| static void | copy_rules (struct queue_ent *qe, const char *rulename) |
| Copy rule from global list into specified queue. | |
| static struct member * | create_queue_member (const char *interface, const char *membername, int penalty, int paused, const char *state_interface) |
| allocate space for new queue member and set fields based on parameters passed | |
| static void | destroy_queue (void *obj) |
| Free queue's member list then its string fields. | |
| static void | device_state_cb (const struct ast_event *event, void *unused) |
| static void | do_hang (struct callattempt *o) |
| common hangup actions | |
| static void | do_print (struct mansession *s, int fd, const char *str) |
| direct ouput to manager or cli with proper terminator | |
| static void | dump_queue_members (struct call_queue *pm_queue) |
| Dump all members in a specific queue to the database. | |
| static void | end_bridge_callback (void *data) |
| static void | end_bridge_callback_data_fixup (struct ast_bridge_config *bconfig, struct ast_channel *originator, struct ast_channel *terminator) |
| static struct callattempt * | find_best (struct callattempt *outgoing) |
| find the entry with the best metric, or NULL | |
| static struct call_queue * | find_queue_by_name_rt (const char *queuename, struct ast_variable *queue_vars, struct ast_config *member_config) |
| Reload a single queue via realtime. | |
| static void | free_members (struct call_queue *q, int all) |
| Iterate through queue's member list and delete them. | |
| static int | get_member_penalty (char *queuename, char *interface) |
| static int | get_member_status (struct call_queue *q, int max_penalty, int min_penalty, enum empty_conditions conditions) |
| Check if members are available. | |
| static char * | handle_queue_add_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_queue_pause_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_queue_reload (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_queue_remove_member (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_queue_reset (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_queue_rule_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static char * | handle_queue_set_member_penalty (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static int | handle_statechange (void *datap) |
| set a member's status based on device state of that member's interface | |
| static void | hangupcalls (struct callattempt *outgoing, struct ast_channel *exception, int cancel_answered_elsewhere) |
| Hang up a list of outgoing calls. | |
| static void | init_queue (struct call_queue *q) |
| Initialize Queue default values. | |
| static void | insert_entry (struct call_queue *q, struct queue_ent *prev, struct queue_ent *new, int *pos) |
| Insert the 'new' entry after the 'prev' entry of queue 'q'. | |
| static int | insert_penaltychange (const char *list_name, const char *content, const int linenum) |
| Change queue penalty by adding rule. | |
| static const char * | int2strat (int strategy) |
| static struct member * | interface_exists (struct call_queue *q, const char *interface) |
| static int | is_our_turn (struct queue_ent *qe) |
| Check if we should start attempting to call queue members. | |
| static int | join_queue (char *queuename, struct queue_ent *qe, enum queue_result *reason) |
| static int | kill_dead_members (void *obj, void *arg, int flags) |
| static int | kill_dead_queues (void *obj, void *arg, int flags) |
| static void | leave_queue (struct queue_ent *qe) |
| Caller leaving queue. | |
| static int | load_module (void) |
| static struct call_queue * | load_realtime_queue (const char *queuename) |
| static int | manager_add_queue_member (struct mansession *s, const struct message *m) |
| static int | manager_pause_queue_member (struct mansession *s, const struct message *m) |
| static int | manager_queue_log_custom (struct mansession *s, const struct message *m) |
| static int | manager_queue_member_penalty (struct mansession *s, const struct message *m) |
| static int | manager_queue_reload (struct mansession *s, const struct message *m) |
| static int | manager_queue_reset (struct mansession *s, const struct message *m) |
| static int | manager_queue_rule_show (struct mansession *s, const struct message *m) |
| static int | manager_queues_show (struct mansession *s, const struct message *m) |
| static int | manager_queues_status (struct mansession *s, const struct message *m) |
| Queue status info via AMI. | |
| static int | manager_queues_summary (struct mansession *s, const struct message *m) |
| Summary of queue info via the AMI. | |
| static int | manager_remove_queue_member (struct mansession *s, const struct message *m) |
| static int | mark_dead_and_unfound (void *obj, void *arg, int flags) |
| static int | mark_member_dead (void *obj, void *arg, int flags) |
| static int | member_cmp_fn (void *obj1, void *obj2, int flags) |
| static int | member_hash_fn (const void *obj, const int flags) |
| static int | num_available_members (struct call_queue *q) |
| Get the number of members available to accept a call. | |
| static void | parse_empty_options (const char *value, enum empty_conditions *empty, int joinempty) |
| static int | play_file (struct ast_channel *chan, const char *filename) |
| static int | pqm_exec (struct ast_channel *chan, 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_global_params (struct ast_config *cfg) |
| static void | queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown) |
| Configure a queue parameter. | |
| static char * | queue_show (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) |
| static void | queue_transfer_destroy (void *data) |
| static void | queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) |
| Log an attended transfer when a queue caller channel is masqueraded. | |
| static struct call_queue * | queue_unref (struct call_queue *q) |
| static void | recalc_holdtime (struct queue_ent *qe, int newholdtime) |
| static void | record_abandoned (struct queue_ent *qe) |
| Record that a caller gave up on waiting in queue. | |
| static int | reload (void) |
| static int | reload_handler (int reload, struct ast_flags *mask, const char *queuename) |
| The command center for all reload operations. | |
| static void | reload_queue_members (void) |
| Reload dynamic queue members persisted into the astdb. | |
| static int | reload_queue_rules (int reload) |
| Reload the rules defined in queuerules.conf. | |
| static int | reload_queues (int reload, struct ast_flags *mask, const char *queuename) |
| reload the queues.conf file | |
| static void | reload_single_member (const char *memberdata, struct call_queue *q) |
| reload information pertaining to a single member | |
| static void | reload_single_queue (struct ast_config *cfg, struct ast_flags *mask, const char *queuename) |
| Reload information pertaining to a particular queue. | |
| static int | remove_from_queue (const char *queuename, const char *interface) |
| Remove member from queue. | |
| static int | ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies) |
| Part 2 of ring_one. | |
| static int | ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies) |
| Place a call to a queue member. | |
| static void | rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause) |
| RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. | |
| static int | rqm_exec (struct ast_channel *chan, 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, int newtalktime) |
| update the queue status | |
| static int | update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value) |
| static void | update_realtime_members (struct call_queue *q) |
| static int | update_status (struct call_queue *q, struct member *m, const int status) |
| set a member's status based on device state of that member's state_interface. | |
| static int | upqm_exec (struct ast_channel *chan, 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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, } |
| static char * | app = "Queue" |
| static char * | app_aqm = "AddQueueMember" |
| static char * | app_pqm = "PauseQueueMember" |
| static char * | app_ql = "QueueLog" |
| static char * | app_rqm = "RemoveQueueMember" |
| static char * | app_upqm = "UnpauseQueueMember" |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static int | autofill_default = 0 |
| queues.conf [general] option | |
| static struct ast_cli_entry | cli_queue [] |
| static struct ast_event_sub * | device_state_sub |
| Subscription to device state change events. | |
| static struct ast_taskprocessor * | devicestate_tps |
| static int | montype_default = 0 |
| queues.conf [general] option | |
| static const char * | pm_family = "Queue/PersistentMembers" |
| Persistent Members astdb family. | |
| static const char | qpm_cmd_usage [] |
| static const char | qsmp_cmd_usage [] |
| 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 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 692 of file app_queue.c.
Referenced by queue_set_param().
| #define ANNOUNCEHOLDTIME_ONCE 2 |
Definition at line 693 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 708 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 707 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 706 of file app_queue.c.
Referenced by queue_set_param().
| #define ANNOUNCEPOSITION_YES 1 |
We announce position
Definition at line 705 of file app_queue.c.
Referenced by init_queue(), queue_set_param(), and say_position().
| #define AST_MAX_WATCHERS 256 |
Definition at line 2834 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 530 of file app_queue.c.
Referenced by init_queue().
| #define DEFAULT_RETRY 5 |
Definition at line 526 of file app_queue.c.
Referenced by init_queue(), and queue_set_param().
| #define DEFAULT_TIMEOUT 15 |
Definition at line 527 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 529 of file app_queue.c.
Referenced by destroy_queue(), init_queue(), and queue_set_param().
| #define MAX_QUEUE_BUCKETS 53 |
Definition at line 532 of file app_queue.c.
Referenced by load_module().
| #define PM_MAX_LEN 8192 |
Definition at line 555 of file app_queue.c.
Referenced by dump_queue_members(), and reload_queue_members().
| #define QUEUE_EVENT_VARIABLES 3 |
Definition at line 694 of file app_queue.c.
Referenced by queue_set_param(), ring_entry(), rna(), send_agent_complete(), and try_calling().
| #define queue_t_ref | ( | a, | |||
| b | ) | queue_ref(a) |
Definition at line 884 of file app_queue.c.
Referenced by leave_queue(), and try_calling().
| #define queue_t_unref | ( | a, | |||
| b | ) | queue_unref(a) |
Definition at line 885 of file app_queue.c.
Referenced by __queues_show(), alloc_queue(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), end_bridge_callback(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), 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_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), unload_module(), and update_queue().
| #define queues_t_link | ( | c, | |||
| q, | |||||
| tag | ) | ao2_t_link(c,q,tag) |
Definition at line 886 of file app_queue.c.
Referenced by find_queue_by_name_rt(), and reload_single_queue().
| #define queues_t_unlink | ( | c, | |||
| q, | |||||
| tag | ) | ao2_t_unlink(c,q,tag) |
Definition at line 887 of file app_queue.c.
Referenced by find_queue_by_name_rt(), leave_queue(), and unload_module().
| #define RECHECK 1 |
Recheck every second to see we we're at the top yet
Definition at line 528 of file app_queue.c.
Referenced by wait_our_turn().
| #define RES_EXISTS (-1) |
Entry already exists
Definition at line 535 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 537 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 538 of file app_queue.c.
Referenced by handle_queue_add_member(), handle_queue_remove_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
| #define RES_OKAY 0 |
Action completed
Definition at line 534 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 536 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 493 of file app_queue.c.
00493 { 00494 QUEUE_STRATEGY_RINGALL = 0, 00495 QUEUE_STRATEGY_LEASTRECENT, 00496 QUEUE_STRATEGY_FEWESTCALLS, 00497 QUEUE_STRATEGY_RANDOM, 00498 QUEUE_STRATEGY_RRMEMORY, 00499 QUEUE_STRATEGY_LINEAR, 00500 QUEUE_STRATEGY_WRANDOM 00501 };
Definition at line 3376 of file app_queue.c.
| enum empty_conditions |
| QUEUE_EMPTY_PENALTY | |
| QUEUE_EMPTY_PAUSED | |
| QUEUE_EMPTY_INUSE | |
| QUEUE_EMPTY_RINGING | |
| QUEUE_EMPTY_UNAVAILABLE | |
| QUEUE_EMPTY_INVALID | |
| QUEUE_EMPTY_UNKNOWN | |
| QUEUE_EMPTY_WRAPUP |
Definition at line 680 of file app_queue.c.
00680 { 00681 QUEUE_EMPTY_PENALTY = (1 << 0), 00682 QUEUE_EMPTY_PAUSED = (1 << 1), 00683 QUEUE_EMPTY_INUSE = (1 << 2), 00684 QUEUE_EMPTY_RINGING = (1 << 3), 00685 QUEUE_EMPTY_UNAVAILABLE = (1 << 4), 00686 QUEUE_EMPTY_INVALID = (1 << 5), 00687 QUEUE_EMPTY_UNKNOWN = (1 << 6), 00688 QUEUE_EMPTY_WRAPUP = (1 << 7), 00689 };
| enum queue_reload_mask |
Definition at line 503 of file app_queue.c.
00503 { 00504 QUEUE_RELOAD_PARAMETERS = (1 << 0), 00505 QUEUE_RELOAD_MEMBER = (1 << 1), 00506 QUEUE_RELOAD_RULES = (1 << 2), 00507 QUEUE_RESET_STATS = (1 << 3), 00508 };
| enum queue_result |
| QUEUE_UNKNOWN | |
| QUEUE_TIMEOUT | |
| QUEUE_JOINEMPTY | |
| QUEUE_LEAVEEMPTY | |
| QUEUE_JOINUNAVAIL | |
| QUEUE_LEAVEUNAVAIL | |
| QUEUE_FULL | |
| QUEUE_CONTINUE |
Definition at line 578 of file app_queue.c.
00578 { 00579 QUEUE_UNKNOWN = 0, 00580 QUEUE_TIMEOUT = 1, 00581 QUEUE_JOINEMPTY = 2, 00582 QUEUE_LEAVEEMPTY = 3, 00583 QUEUE_JOINUNAVAIL = 4, 00584 QUEUE_LEAVEUNAVAIL = 5, 00585 QUEUE_FULL = 6, 00586 QUEUE_CONTINUE = 7, 00587 };
Definition at line 603 of file app_queue.c.
00603 { 00604 TIMEOUT_PRIORITY_APP, 00605 TIMEOUT_PRIORITY_CONF, 00606 };
| 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 6083 of file app_queue.c.
References ao2_container_count(), ao2_iterator_destroy(), AO2_ITERATOR_DONTLOCK, ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_category_browse(), ast_check_realtime(), ast_config_destroy(), ast_devstate2str(), ast_load_realtime_multientry(), ast_str_alloca, ast_str_append(), ast_str_buffer(), ast_str_set(), ast_strlen_zero(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, CLI_SHOWUSAGE, CLI_SUCCESS, call_queue::count, do_print(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::pos, queue_ent::prio, queue_t_unref, queues, member::realtime, call_queue::realtime, SENTINEL, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, call_queue::talktime, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
06084 { 06085 struct call_queue *q; 06086 struct ast_str *out = ast_str_alloca(240); 06087 int found = 0; 06088 time_t now = time(NULL); 06089 struct ao2_iterator queue_iter; 06090 struct ao2_iterator mem_iter; 06091 06092 if (argc != 2 && argc != 3) 06093 return CLI_SHOWUSAGE; 06094 06095 if (argc == 3) { /* specific queue */ 06096 if ((q = load_realtime_queue(argv[2]))) { 06097 queue_t_unref(q, "Done with temporary pointer"); 06098 } 06099 } else if (ast_check_realtime("queues")) { 06100 /* This block is to find any queues which are defined in realtime but 06101 * which have not yet been added to the in-core container 06102 */ 06103 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", SENTINEL); 06104 char *queuename; 06105 if (cfg) { 06106 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 06107 if ((q = load_realtime_queue(queuename))) { 06108 queue_t_unref(q, "Done with temporary pointer"); 06109 } 06110 } 06111 ast_config_destroy(cfg); 06112 } 06113 } 06114 06115 queue_iter = ao2_iterator_init(queues, AO2_ITERATOR_DONTLOCK); 06116 ao2_lock(queues); 06117 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06118 float sl; 06119 struct call_queue *realtime_queue = NULL; 06120 06121 ao2_lock(q); 06122 /* This check is to make sure we don't print information for realtime 06123 * queues which have been deleted from realtime but which have not yet 06124 * been deleted from the in-core container 06125 */ 06126 if (q->realtime && !(realtime_queue = load_realtime_queue(q->name))) { 06127 ao2_unlock(q); 06128 queue_t_unref(q, "Done with iterator"); 06129 continue; 06130 } else if (q->realtime) { 06131 queue_t_unref(realtime_queue, "Queue is already in memory"); 06132 } 06133 if (argc == 3 && strcasecmp(q->name, argv[2])) { 06134 ao2_unlock(q); 06135 queue_t_unref(q, "Done with iterator"); 06136 continue; 06137 } 06138 found = 1; 06139 06140 ast_str_set(&out, 0, "%s has %d calls (max ", q->name, q->count); 06141 if (q->maxlen) 06142 ast_str_append(&out, 0, "%d", q->maxlen); 06143 else 06144 ast_str_append(&out, 0, "unlimited"); 06145 sl = 0; 06146 if (q->callscompleted > 0) 06147 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 06148 ast_str_append(&out, 0, ") in '%s' strategy (%ds holdtime, %ds talktime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds", 06149 int2strat(q->strategy), q->holdtime, q->talktime, q->weight, 06150 q->callscompleted, q->callsabandoned,sl,q->servicelevel); 06151 do_print(s, fd, ast_str_buffer(out)); 06152 if (!ao2_container_count(q->members)) 06153 do_print(s, fd, " No Members"); 06154 else { 06155 struct member *mem; 06156 06157 do_print(s, fd, " Members: "); 06158 mem_iter = ao2_iterator_init(q->members, 0); 06159 while ((mem = ao2_iterator_next(&mem_iter))) { 06160 ast_str_set(&out, 0, " %s", mem->membername); 06161 if (strcasecmp(mem->membername, mem->interface)) { 06162 ast_str_append(&out, 0, " (%s)", mem->interface); 06163 } 06164 if (mem->penalty) 06165 ast_str_append(&out, 0, " with penalty %d", mem->penalty); 06166 ast_str_append(&out, 0, "%s%s%s (%s)", 06167 mem->dynamic ? " (dynamic)" : "", 06168 mem->realtime ? " (realtime)" : "", 06169 mem->paused ? " (paused)" : "", 06170 ast_devstate2str(mem->status)); 06171 if (mem->calls) 06172 ast_str_append(&out, 0, " has taken %d calls (last was %ld secs ago)", 06173 mem->calls, (long) (time(NULL) - mem->lastcall)); 06174 else 06175 ast_str_append(&out, 0, " has taken no calls yet"); 06176 do_print(s, fd, ast_str_buffer(out)); 06177 ao2_ref(mem, -1); 06178 } 06179 ao2_iterator_destroy(&mem_iter); 06180 } 06181 if (!q->head) 06182 do_print(s, fd, " No Callers"); 06183 else { 06184 struct queue_ent *qe; 06185 int pos = 1; 06186 06187 do_print(s, fd, " Callers: "); 06188 for (qe = q->head; qe; qe = qe->next) { 06189 ast_str_set(&out, 0, " %d. %s (wait: %ld:%2.2ld, prio: %d)", 06190 pos++, qe->chan->name, (long) (now - qe->start) / 60, 06191 (long) (now - qe->start) % 60, qe->prio); 06192 do_print(s, fd, ast_str_buffer(out)); 06193 } 06194 } 06195 do_print(s, fd, ""); /* blank line between entries */ 06196 ao2_unlock(q); 06197 queue_t_unref(q, "Done with iterator"); /* Unref the iterator's reference */ 06198 } 06199 ao2_iterator_destroy(&queue_iter); 06200 ao2_unlock(queues); 06201 if (!found) { 06202 if (argc == 3) 06203 ast_str_set(&out, 0, "No such queue: %s.", argv[2]); 06204 else 06205 ast_str_set(&out, 0, "No queues."); 06206 do_print(s, fd, ast_str_buffer(out)); 06207 } 06208 return CLI_SUCCESS; 06209 }
| static void __reg_module | ( | void | ) | [static] |
Definition at line 7292 of file app_queue.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 7292 of file app_queue.c.
| static int add_to_queue | ( | const char * | queuename, | |
| const char * | interface, | |||
| const char * | membername, | |||
| int | penalty, | |||
| int | paused, | |||
| int | dump, | |||
| const char * | state_interface | |||
| ) | [static] |
Add member to queue.
| RES_NOT_DYNAMIC | when they aren't a RT member | |
| RES_NOSUCHQUEUE | queue does not exist | |
| RES_OKAY | added member from queue | |
| RES_EXISTS | queue exists but no members | |
| RES_OUT_OF_MEMORY | queue exists but not enough memory to create member |
Definition at line 4455 of file app_queue.c.
References ao2_link, ao2_lock(), ao2_ref, ao2_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), manager_event, call_queue::membercount, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, queues, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and member::status.
Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().
04456 { 04457 struct call_queue *q; 04458 struct member *new_member, *old_member; 04459 int res = RES_NOSUCHQUEUE; 04460 04461 /*! \note Ensure the appropriate realtime queue is loaded. Note that this 04462 * short-circuits if the queue is already in memory. */ 04463 if (!(q = load_realtime_queue(queuename))) 04464 return res; 04465 04466 ao2_lock(queues); 04467 04468 ao2_lock(q); 04469 if ((old_member = interface_exists(q, interface)) == NULL) { 04470 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) { 04471 new_member->dynamic = 1; 04472 ao2_link(q->members, new_member); 04473 q->membercount++; 04474 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 04475 "Queue: %s\r\n" 04476 "Location: %s\r\n" 04477 "MemberName: %s\r\n" 04478 "Membership: %s\r\n" 04479 "Penalty: %d\r\n" 04480 "CallsTaken: %d\r\n" 04481 "LastCall: %d\r\n" 04482 "Status: %d\r\n" 04483 "Paused: %d\r\n", 04484 q->name, new_member->interface, new_member->membername, 04485 "dynamic", 04486 new_member->penalty, new_member->calls, (int) new_member->lastcall, 04487 new_member->status, new_member->paused); 04488 04489 ao2_ref(new_member, -1); 04490 new_member = NULL; 04491 04492 if (dump) 04493 dump_queue_members(q); 04494 04495 res = RES_OKAY; 04496 } else { 04497 res = RES_OUTOFMEMORY; 04498 } 04499 } else { 04500 ao2_ref(old_member, -1); 04501 res = RES_EXISTS; 04502 } 04503 ao2_unlock(q); 04504 ao2_unlock(queues); 04505 04506 return res; 04507 }
| static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static, read] |
Definition at line 1672 of file app_queue.c.
References ao2_t_alloc, ast_string_field_init, ast_string_field_set, destroy_queue(), and queue_t_unref.
Referenced by find_queue_by_name_rt(), and reload_single_queue().
01673 { 01674 struct call_queue *q; 01675 01676 if ((q = ao2_t_alloc(sizeof(*q), destroy_queue, "Allocate queue"))) { 01677 if (ast_string_field_init(q, 64)) { 01678 queue_t_unref(q, "String field allocation failed"); 01679 return NULL; 01680 } 01681 ast_string_field_set(q, name, queuename); 01682 } 01683 return q; 01684 }
| static int aqm_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
AddQueueMember application.
Definition at line 4893 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_strlen_zero(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and ast_channel::uniqueid.
Referenced by load_module().
04894 { 04895 int res=-1; 04896 char *parse, *temppos = NULL; 04897 AST_DECLARE_APP_ARGS(args, 04898 AST_APP_ARG(queuename); 04899 AST_APP_ARG(interface); 04900 AST_APP_ARG(penalty); 04901 AST_APP_ARG(options); 04902 AST_APP_ARG(membername); 04903 AST_APP_ARG(state_interface); 04904 ); 04905 int penalty = 0; 04906 04907 if (ast_strlen_zero(data)) { 04908 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[,interface[,penalty[,options[,membername[,stateinterface]]]]])\n"); 04909 return -1; 04910 } 04911 04912 parse = ast_strdupa(data); 04913 04914 AST_STANDARD_APP_ARGS(args, parse); 04915 04916 if (ast_strlen_zero(args.interface)) { 04917 args.interface = ast_strdupa(chan->name); 04918 temppos = strrchr(args.interface, '-'); 04919 if (temppos) 04920 *temppos = '\0'; 04921 } 04922 04923 if (!ast_strlen_zero(args.penalty)) { 04924 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) { 04925 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 04926 penalty = 0; 04927 } 04928 } 04929 04930 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) { 04931 case RES_OKAY: 04932 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 04933 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 04934 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 04935 res = 0; 04936 break; 04937 case RES_EXISTS: 04938 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 04939 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 04940 res = 0; 04941 break; 04942 case RES_NOSUCHQUEUE: 04943 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 04944 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 04945 res = 0; 04946 break; 04947 case RES_OUTOFMEMORY: 04948 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 04949 break; 04950 } 04951 04952 return res; 04953 }
| 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 3480 of file app_queue.c.
References ast_channel_datastore_find(), and queue_transfer_info.
Referenced by try_calling().
03481 { 03482 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 03483 }
| 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 3319 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().
03320 { 03321 if ((qe->max_penalty && (mem->penalty > qe->max_penalty)) || (qe->min_penalty && (mem->penalty < qe->min_penalty))) 03322 return -1; 03323 03324 switch (q->strategy) { 03325 case QUEUE_STRATEGY_RINGALL: 03326 /* Everyone equal, except for penalty */ 03327 tmp->metric = mem->penalty * 1000000; 03328 break; 03329 case QUEUE_STRATEGY_LINEAR: 03330 if (pos < qe->linpos) { 03331 tmp->metric = 1000 + pos; 03332 } else { 03333 if (pos > qe->linpos) 03334 /* Indicate there is another priority */ 03335 qe->linwrapped = 1; 03336 tmp->metric = pos; 03337 } 03338 tmp->metric += mem->penalty * 1000000; 03339 break; 03340 case QUEUE_STRATEGY_RRMEMORY: 03341 if (pos < q->rrpos) { 03342 tmp->metric = 1000 + pos; 03343 } else { 03344 if (pos > q->rrpos) 03345 /* Indicate there is another priority */ 03346 q->wrapped = 1; 03347 tmp->metric = pos; 03348 } 03349 tmp->metric += mem->penalty * 1000000; 03350 break; 03351 case QUEUE_STRATEGY_RANDOM: 03352 tmp->metric = ast_random() % 1000; 03353 tmp->metric += mem->penalty * 1000000; 03354 break; 03355 case QUEUE_STRATEGY_WRANDOM: 03356 tmp->metric = ast_random() % ((1 + mem->penalty) * 1000); 03357 break; 03358 case QUEUE_STRATEGY_FEWESTCALLS: 03359 tmp->metric = mem->calls; 03360 tmp->metric += mem->penalty * 1000000; 03361 break; 03362 case QUEUE_STRATEGY_LEASTRECENT: 03363 if (!mem->lastcall) 03364 tmp->metric = 0; 03365 else 03366 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 03367 tmp->metric += mem->penalty * 1000000; 03368 break; 03369 default: 03370 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 03371 break; 03372 } 03373 return 0; 03374 }
| static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 1243 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, call_queue::members, call_queue::talktime, and call_queue::wrapuptime.
Referenced by clear_stats(), and find_queue_by_name_rt().
01244 { 01245 q->holdtime = 0; 01246 q->callscompleted = 0; 01247 q->callsabandoned = 0; 01248 q->callscompletedinsl = 0; 01249 q->wrapuptime = 0; 01250 q->talktime = 0; 01251 01252 if (q->members) { 01253 struct member *mem; 01254 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01255 while ((mem = ao2_iterator_next(&mem_iter))) { 01256 mem->calls = 0; 01257 ao2_ref(mem, -1); 01258 } 01259 ao2_iterator_destroy(&mem_iter); 01260 } 01261 }
| static int clear_stats | ( | const char * | queuename | ) | [static] |
Facilitates resetting statistics for a queue.
This function actually does not reset any statistics, but rather finds a call_queue struct which corresponds to the passed-in queue name and passes that structure to the clear_queue function. If no queuename is passed in, then all queues will have their statistics reset.
| queuename | The name of the queue to reset the statistics for. If this is NULL or zero-length, then this means to reset the statistics for all queues |
| void |
Definition at line 6024 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_t_iterator_next, ao2_unlock(), ast_strlen_zero(), clear_queue(), call_queue::name, queue_t_unref, and queues.
Referenced by reload_handler().
06025 { 06026 struct call_queue *q; 06027 struct ao2_iterator queue_iter = ao2_iterator_init(queues, 0); 06028 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06029 ao2_lock(q); 06030 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) 06031 clear_queue(q); 06032 ao2_unlock(q); 06033 queue_t_unref(q, "Done with iterator"); 06034 } 06035 ao2_iterator_destroy(&queue_iter); 06036 return 0; 06037 }
| static int compare_weight | ( | struct call_queue * | rq, | |
| struct member * | member | |||
| ) | [static] |
Definition at line 2371 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().
02372 { 02373 struct call_queue *q; 02374 struct member *mem; 02375 int found = 0; 02376 struct ao2_iterator queue_iter; 02377 02378 /* q's lock and rq's lock already set by try_calling() 02379 * to solve deadlock */ 02380 queue_iter = ao2_iterator_init(queues, 0); 02381 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 02382 if (q == rq) { /* don't check myself, could deadlock */ 02383 queue_t_unref(q, "Done with iterator"); 02384 continue; 02385 } 02386 ao2_lock(q); 02387 if (q->count && q->members) { 02388 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 02389 ast_debug(1, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 02390 if (q->weight > rq->weight && q->count >= num_available_members(q)) { 02391 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); 02392 found = 1; 02393 } 02394 ao2_ref(mem, -1); 02395 } 02396 } 02397 ao2_unlock(q); 02398 queue_t_unref(q, "Done with iterator"); 02399 if (found) { 02400 break; 02401 } 02402 } 02403 ao2_iterator_destroy(&queue_iter); 02404 return found; 02405 }
| static char* complete_queue | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6211 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(), complete_queue_show(), handle_queue_reload(), and handle_queue_reset().
06212 { 06213 struct call_queue *q; 06214 char *ret = NULL; 06215 int which = 0; 06216 int wordlen = strlen(word); 06217 struct ao2_iterator queue_iter; 06218 06219 queue_iter = ao2_iterator_init(queues, 0); 06220 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06221 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 06222 ret = ast_strdup(q->name); 06223 queue_t_unref(q, "Done with iterator"); 06224 break; 06225 } 06226 queue_t_unref(q, "Done with iterator"); 06227 } 06228 ao2_iterator_destroy(&queue_iter); 06229 06230 return ret; 06231 }
| static char* complete_queue_add_member | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6643 of file app_queue.c.
References ast_malloc, ast_strdup, complete_queue(), and num.
Referenced by handle_queue_add_member().
06644 { 06645 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 06646 switch (pos) { 06647 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 06648 return NULL; 06649 case 4: /* only one possible match, "to" */ 06650 return state == 0 ? ast_strdup("to") : NULL; 06651 case 5: /* <queue> */ 06652 return complete_queue(line, word, pos, state); 06653 case 6: /* only one possible match, "penalty" */ 06654 return state == 0 ? ast_strdup("penalty") : NULL; 06655 case 7: 06656 if (state < 100) { /* 0-99 */ 06657 char *num; 06658 if ((num = ast_malloc(3))) { 06659 sprintf(num, "%d", state); 06660 } 06661 return num; 06662 } else { 06663 return NULL; 06664 } 06665 case 8: /* only one possible match, "as" */ 06666 return state == 0 ? ast_strdup("as") : NULL; 06667 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 06668 return NULL; 06669 default: 06670 return NULL; 06671 } 06672 }
| static char* complete_queue_pause_member | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6864 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_pause_member().
06865 { 06866 /* 0 - queue; 1 - pause; 2 - member; 3 - <interface>; 4 - queue; 5 - <queue>; 6 - reason; 7 - <reason> */ 06867 switch (pos) { 06868 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 06869 return NULL; 06870 case 4: /* only one possible match, "queue" */ 06871 return state == 0 ? ast_strdup("queue") : NULL; 06872 case 5: /* <queue> */ 06873 return complete_queue(line, word, pos, state); 06874 case 6: /* "reason" */ 06875 return state == 0 ? ast_strdup("reason") : NULL; 06876 case 7: /* Can't autocomplete a reason, since it's 100% customizeable */ 06877 return NULL; 06878 default: 06879 return NULL; 06880 } 06881 }
| static char* complete_queue_remove_member | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6773 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().
06774 { 06775 int which = 0; 06776 struct call_queue *q; 06777 struct member *m; 06778 struct ao2_iterator queue_iter; 06779 struct ao2_iterator mem_iter; 06780 int wordlen = strlen(word); 06781 06782 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 06783 if (pos > 5 || pos < 3) 06784 return NULL; 06785 if (pos == 4) /* only one possible match, 'from' */ 06786 return (state == 0 ? ast_strdup("from") : NULL); 06787 06788 if (pos == 5) /* No need to duplicate code */ 06789 return complete_queue(line, word, pos, state); 06790 06791 /* here is the case for 3, <member> */ 06792 queue_iter = ao2_iterator_init(queues, 0); 06793 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06794 ao2_lock(q); 06795 mem_iter = ao2_iterator_init(q->members, 0); 06796 while ((m = ao2_iterator_next(&mem_iter))) { 06797 if (!strncasecmp(word, m->membername, wordlen) && ++which > state) { 06798 char *tmp; 06799 ao2_unlock(q); 06800 tmp = ast_strdup(m->interface); 06801 ao2_ref(m, -1); 06802 queue_t_unref(q, "Done with iterator, returning interface name"); 06803 ao2_iterator_destroy(&mem_iter); 06804 ao2_iterator_destroy(&queue_iter); 06805 return tmp; 06806 } 06807 ao2_ref(m, -1); 06808 } 06809 ao2_iterator_destroy(&mem_iter); 06810 ao2_unlock(q); 06811 queue_t_unref(q, "Done with iterator"); 06812 } 06813 ao2_iterator_destroy(&queue_iter); 06814 06815 return NULL; 06816 }
| static char* complete_queue_rule_show | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6997 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().
06998 { 06999 int which = 0; 07000 struct rule_list *rl_iter; 07001 int wordlen = strlen(word); 07002 char *ret = NULL; 07003 if (pos != 3) /* Wha? */ { 07004 return NULL; 07005 } 07006 07007 AST_LIST_LOCK(&rule_lists); 07008 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 07009 if (!strncasecmp(word, rl_iter->name, wordlen) && ++which > state) { 07010 ret = ast_strdup(rl_iter->name); 07011 break; 07012 } 07013 } 07014 AST_LIST_UNLOCK(&rule_lists); 07015 07016 return ret; 07017 }
| static char* complete_queue_set_member_penalty | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6934 of file app_queue.c.
References ast_strdup, and complete_queue().
Referenced by handle_queue_set_member_penalty().
06935 { 06936 /* 0 - queue; 1 - set; 2 - penalty; 3 - <penalty>; 4 - on; 5 - <member>; 6 - in; 7 - <queue>;*/ 06937 switch (pos) { 06938 case 4: 06939 if (state == 0) { 06940 return ast_strdup("on"); 06941 } else { 06942 return NULL; 06943 } 06944 case 6: 06945 if (state == 0) { 06946 return ast_strdup("in"); 06947 } else { 06948 return NULL; 06949 } 06950 case 7: 06951 return complete_queue(line, word, pos, state); 06952 default: 06953 return NULL; 06954 } 06955 }
| static char* complete_queue_show | ( | const char * | line, | |
| const char * | word, | |||
| int | pos, | |||
| int | state | |||
| ) | [static] |
Definition at line 6233 of file app_queue.c.
References complete_queue().
Referenced by queue_show().
06234 { 06235 if (pos == 2) 06236 return complete_queue(line, word, pos, state); 06237 return NULL; 06238 }
| static int compress_char | ( | const char | c | ) | [static] |
Definition at line 1142 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 4990 of file app_queue.c.
References ast_calloc, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strlen_zero(), call_queue::defaultrule, LOG_ERROR, penalty_rule::max_relative, penalty_rule::max_value, penalty_rule::min_relative, penalty_rule::min_value, rule_list::name, queue_ent::parent, queue_ent::qe_rules, rule_list::rules, and penalty_rule::time.
Referenced by queue_exec().
04991 { 04992 struct penalty_rule *pr_iter; 04993 struct rule_list *rl_iter; 04994 const char *tmp = ast_strlen_zero(rulename) ? qe->parent->defaultrule : rulename; 04995 AST_LIST_LOCK(&rule_lists); 04996 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 04997 if (!strcasecmp(rl_iter->name, tmp)) 04998 break; 04999 } 05000 if (rl_iter) { 05001 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 05002 struct penalty_rule *new_pr = ast_calloc(1, sizeof(*new_pr)); 05003 if (!new_pr) { 05004 ast_log(LOG_ERROR, "Memory allocation error when copying penalty rules! Aborting!\n"); 05005 AST_LIST_UNLOCK(&rule_lists); 05006 break; 05007 } 05008 new_pr->time = pr_iter->time; 05009 new_pr->max_value = pr_iter->max_value; 05010 new_pr->min_value = pr_iter->min_value; 05011 new_pr->max_relative = pr_iter->max_relative; 05012 new_pr->min_relative = pr_iter->min_relative; 05013 AST_LIST_INSERT_TAIL(&qe->qe_rules, new_pr, list); 05014 } 05015 } 05016 AST_LIST_UNLOCK(&rule_lists); 05017 }
| 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 1117 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_single_member(), and rt_handle_member_record().
01118 { 01119 struct member *cur; 01120 01121 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 01122 cur->penalty = penalty; 01123 cur->paused = paused; 01124 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 01125 if (!ast_strlen_zero(state_interface)) 01126 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); 01127 else 01128 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface)); 01129 if (!ast_strlen_zero(membername)) 01130 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 01131 else 01132 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 01133 if (!strchr(cur->interface, '/')) 01134 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 01135 cur->status = ast_device_state(cur->state_interface); 01136 } 01137 01138 return cur; 01139 }
| static void destroy_queue | ( | void * | obj | ) | [static] |
Free queue's member list then its string fields.
Definition at line 1658 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().
01659 { 01660 struct call_queue *q = obj; 01661 int i; 01662 01663 free_members(q, 1); 01664 ast_string_field_free_memory(q); 01665 for (i = 0; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 01666 if (q->sound_periodicannounce[i]) 01667 free(q->sound_periodicannounce[i]); 01668 } 01669 ao2_ref(q->members, -1); 01670 }
| static void device_state_cb | ( | const struct ast_event * | event, | |
| void * | unused | |||
| ) | [static] |
Definition at line 1090 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().
01091 { 01092 enum ast_device_state state; 01093 const char *device; 01094 struct statechange *sc; 01095 size_t datapsize; 01096 01097 state = ast_event_get_ie_uint(event, AST_EVENT_IE_STATE); 01098 device = ast_event_get_ie_str(event, AST_EVENT_IE_DEVICE); 01099 01100 if (ast_strlen_zero(device)) { 01101 ast_log(LOG_ERROR, "Received invalid event that had no device IE\n"); 01102 return; 01103 } 01104 datapsize = sizeof(*sc) + strlen(device) + 1; 01105 if (!(sc = ast_calloc(1, datapsize))) { 01106 ast_log(LOG_ERROR, "failed to calloc a state change struct\n"); 01107 return; 01108 } 01109 sc->state = state; 01110 strcpy(sc->dev, device); 01111 if (ast_taskprocessor_push(devicestate_tps, handle_statechange, sc) < 0) { 01112 ast_free(sc); 01113 } 01114 }
| static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 2408 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry(), and wait_for_answer().
02409 { 02410 o->stillgoing = 0; 02411 ast_hangup(o->chan); 02412 o->chan = NULL; 02413 }
| 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 6069 of file app_queue.c.
References ast_cli(), and astman_append().
Referenced by __queues_show().
06070 { 06071 if (s) 06072 astman_append(s, "%s\r\n", str); 06073 else 06074 ast_cli(fd, "%s\n", str); 06075 }
| 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 4356 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().
04357 { 04358 struct member *cur_member; 04359 char value[PM_MAX_LEN]; 04360 int value_len = 0; 04361 int res; 04362 struct ao2_iterator mem_iter; 04363 04364 memset(value, 0, sizeof(value)); 04365 04366 if (!pm_queue) 04367 return; 04368 04369 mem_iter = ao2_iterator_init(pm_queue->members, 0); 04370 while ((cur_member = ao2_iterator_next(&mem_iter))) { 04371 if (!cur_member->dynamic) { 04372 ao2_ref(cur_member, -1); 04373 continue; 04374 } 04375 04376 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s", 04377 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface); 04378 04379 ao2_ref(cur_member, -1); 04380 04381 if (res != strlen(value + value_len)) { 04382 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 04383 break; 04384 } 04385 value_len += res; 04386 } 04387 ao2_iterator_destroy(&mem_iter); 04388 04389 if (value_len && !cur_member) { 04390 if (ast_db_put(pm_family, pm_queue->name, value)) 04391 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 04392 } else 04393 /* Delete the entry if the queue is empty or there is an error */ 04394 ast_db_del(pm_family, pm_queue->name); 04395 }
| static void end_bridge_callback | ( | void * | data | ) | [static] |
Definition at line 3527 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().
03528 { 03529 struct queue_end_bridge *qeb = data; 03530 struct call_queue *q = qeb->q; 03531 struct ast_channel *chan = qeb->chan; 03532 03533 if (ao2_ref(qeb, -1) == 1) { 03534 ao2_lock(q); 03535 set_queue_variables(q, chan); 03536 ao2_unlock(q); 03537 /* This unrefs the reference we made in try_calling when we allocated qeb */ 03538 queue_t_unref(q, "Expire bridge_config reference"); 03539 } 03540 }
| static void end_bridge_callback_data_fixup | ( | struct ast_bridge_config * | bconfig, | |
| struct ast_channel * | originator, | |||
| struct ast_channel * | terminator | |||
| ) | [static] |
Definition at line 3520 of file app_queue.c.
References ao2_ref, queue_end_bridge::chan, and ast_bridge_config::end_bridge_callback_data.
Referenced by try_calling().
03521 { 03522 struct queue_end_bridge *qeb = bconfig->end_bridge_callback_data; 03523 ao2_ref(qeb, +1); 03524 qeb->chan = originator; 03525 }
| static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static, read] |
find the entry with the best metric, or NULL
Definition at line 2621 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
Referenced by ring_one(), store_next_lin(), and store_next_rr().
02622 { 02623 struct callattempt *best = NULL, *cur; 02624 02625 for (cur = outgoing; cur; cur = cur->q_next) { 02626 if (cur->stillgoing && /* Not already done */ 02627 !cur->chan && /* Isn't already going */ 02628 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 02629 best = cur; 02630 } 02631 } 02632 02633 return best; 02634 }
| 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 1696 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, rt_handle_member_record(), S_OR, strat2int(), call_queue::strategy, and ast_variable::value.
Referenced by load_realtime_queue().
01697 { 01698 struct ast_variable *v; 01699 struct call_queue *q, tmpq = { 01700 .name = queuename, 01701 }; 01702 struct member *m; 01703 struct ao2_iterator mem_iter; 01704 char *interface = NULL; 01705 const char *tmp_name; 01706 char *tmp; 01707 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 01708 01709 /* Static queues override realtime. */ 01710 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Check if static queue exists"))) { 01711 ao2_lock(q); 01712 if (!q->realtime) { 01713 if (q->dead) { 01714 ao2_unlock(q); 01715 queue_t_unref(q, "Queue is dead; can't return it"); 01716 return NULL; 01717 } else { 01718 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 01719 ao2_unlock(q); 01720 return q; 01721 } 01722 } 01723 } else if (!member_config) 01724 /* Not found in the list, and it's not realtime ... */ 01725 return NULL; 01726 01727 /* Check if queue is defined in realtime. */ 01728 if (!queue_vars) { 01729 /* Delete queue from in-core list if it has been deleted in realtime. */ 01730 if (q) { 01731 /*! \note Hmm, can't seem to distinguish a DB failure from a not 01732 found condition... So we might delete an in-core queue 01733 in case of DB failure. */ 01734 ast_debug(1, "Queue %s not found in realtime.\n", queuename); 01735 01736 q->dead = 1; 01737 /* Delete if unused (else will be deleted when last caller leaves). */ 01738 queues_t_unlink(queues, q, "Unused; removing from container"); 01739 ao2_unlock(q); 01740 queue_t_unref(q, "Queue is dead; can't return it"); 01741 } 01742 return NULL; 01743 } 01744 01745 /* Create a new queue if an in-core entry does not exist yet. */ 01746 if (!q) { 01747 struct ast_variable *tmpvar = NULL; 01748 if (!(q = alloc_queue(queuename))) 01749 return NULL; 01750 ao2_lock(q); 01751 clear_queue(q); 01752 q->realtime = 1; 01753 q->membercount = 0; 01754 /*Before we initialize the queue, we need to set the strategy, so that linear strategy 01755 * will allocate the members properly 01756 */ 01757 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { 01758 if (!strcasecmp(tmpvar->name, "strategy")) { 01759 q->strategy = strat2int(tmpvar->value); 01760 if (q->strategy < 0) { 01761 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01762 tmpvar->value, q->name); 01763 q->strategy = QUEUE_STRATEGY_RINGALL; 01764 } 01765 break; 01766 } 01767 } 01768 /* We traversed all variables and didn't find a strategy */ 01769 if (!tmpvar) 01770 q->strategy = QUEUE_STRATEGY_RINGALL; 01771 queues_t_link(queues, q, "Add queue to container"); 01772 } 01773 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 01774 01775 memset(tmpbuf, 0, sizeof(tmpbuf)); 01776 for (v = queue_vars; v; v = v->next) { 01777 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 01778 if ((tmp = strchr(v->name, '_'))) { 01779 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 01780 tmp_name = tmpbuf; 01781 tmp = tmpbuf; 01782 while ((tmp = strchr(tmp, '_'))) 01783 *tmp++ = '-'; 01784 } else 01785 tmp_name = v->name; 01786 01787 if (!ast_strlen_zero(v->value)) { 01788 /* Don't want to try to set the option if the value is empty */ 01789 queue_set_param(q, tmp_name, v->value, -1, 0); 01790 } 01791 } 01792 01793 /* Temporarily set realtime members dead so we can detect deleted ones. 01794 * Also set the membercount correctly for realtime*/ 01795 mem_iter = ao2_iterator_init(q->members, 0); 01796 while ((m = ao2_iterator_next(&mem_iter))) { 01797 q->membercount++; 01798 if (m->realtime) 01799 m->dead = 1; 01800 ao2_ref(m, -1); 01801 } 01802 ao2_iterator_destroy(&mem_iter); 01803 01804 while ((interface = ast_category_browse(member_config, interface))) { 01805 rt_handle_member_record(q, interface, 01806 ast_variable_retrieve(member_config, interface, "uniqueid"), 01807 S_OR(ast_variable_retrieve(member_config, interface, "membername"),interface), 01808 ast_variable_retrieve(member_config, interface, "penalty"), 01809 ast_variable_retrieve(member_config, interface, "paused"), 01810 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface)); 01811 } 01812 01813 /* Delete all realtime members that have been deleted in DB. */ 01814 mem_iter = ao2_iterator_init(q->members, 0); 01815 while ((m = ao2_iterator_next(&mem_iter))) { 01816 if (m->dead) { 01817 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 01818 ao2_unlink(q->members, m); 01819 q->membercount--; 01820 } 01821 ao2_ref(m, -1); 01822 } 01823 ao2_iterator_destroy(&mem_iter); 01824 01825 ao2_unlock(q); 01826 01827 return q; 01828 }
| static void free_members | ( | struct call_queue * | q, | |
| int | all | |||
| ) | [static] |
Iterate through queue's member list and delete them.
Definition at line 1641 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_ref, ao2_unlink, member::dynamic, call_queue::membercount, and call_queue::members.
Referenced by destroy_queue().
01642 { 01643 /* Free non-dynamic members */ 01644 struct member *cur; 01645 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01646 01647 while ((cur = ao2_iterator_next(&mem_iter))) { 01648 if (all || !cur->dynamic) { 01649 ao2_unlink(q->members, cur); 01650 q->membercount--; 01651 } 01652 ao2_ref(cur, -1); 01653 } 01654 ao2_iterator_destroy(&mem_iter); 01655 }
| static int get_member_penalty | ( | char * | queuename, | |
| char * | interface | |||
| ) | [static] |
Definition at line 4635 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().
04636 { 04637 int foundqueue = 0, penalty; 04638 struct call_queue *q, tmpq = { 04639 .name = queuename, 04640 }; 04641 struct member *mem; 04642 04643 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Search for queue"))) { 04644 foundqueue = 1; 04645 ao2_lock(q); 04646 if ((mem = interface_exists(q, interface))) { 04647 penalty = mem->penalty; 04648 ao2_ref(mem, -1); 04649 ao2_unlock(q); 04650 queue_t_unref(q, "Search complete"); 04651 return penalty; 04652 } 04653 ao2_unlock(q); 04654 queue_t_unref(q, "Search complete"); 04655 } 04656 04657 /* some useful debuging */ 04658 if (foundqueue) 04659 ast_log (LOG_ERROR, "Invalid queuename\n"); 04660 else 04661 ast_log (LOG_ERROR, "Invalid interface\n"); 04662 04663 return RESULT_FAILURE; 04664 }
| static int get_member_status | ( | struct call_queue * | q, | |
| int | max_penalty, | |||
| int | min_penalty, | |||
| enum empty_conditions | conditions | |||
| ) | [static] |
Check if members are available.
This function checks to see if members are available to be called. If any member is available, the function immediately returns 0. If no members are available, then -1 is returned.
Definition at line 951 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlock(), ast_debug, AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, member::lastcall, member::membername, call_queue::members, member::paused, member::penalty, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, QUEUE_EMPTY_WRAPUP, member::status, and call_queue::wrapuptime.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
00952 { 00953 struct member *member; 00954 struct ao2_iterator mem_iter; 00955 00956 ao2_lock(q); 00957 mem_iter = ao2_iterator_init(q->members, 0); 00958 for (; (member = ao2_iterator_next(&mem_iter)); ao2_ref(member, -1)) { 00959 if ((max_penalty && (member->penalty > max_penalty)) || (min_penalty && (member->penalty < min_penalty))) { 00960 if (conditions & QUEUE_EMPTY_PENALTY) { 00961 ast_debug(4, "%s is unavailable because his penalty is not between %d and %d\n", member->membername, min_penalty, max_penalty); 00962 continue; 00963 } 00964 } 00965 00966 switch (member->status) { 00967 case AST_DEVICE_INVALID: 00968 if (conditions & QUEUE_EMPTY_INVALID) { 00969 ast_debug(4, "%s is unavailable because his device state is 'invalid'\n", member->membername); 00970 break; 00971 } 00972 case AST_DEVICE_UNAVAILABLE: 00973 if (conditions & QUEUE_EMPTY_UNAVAILABLE) { 00974 ast_debug(4, "%s is unavailable because his device state is 'unavailable'\n", member->membername); 00975 break; 00976 } 00977 case AST_DEVICE_INUSE: 00978 if (conditions & QUEUE_EMPTY_INUSE) { 00979 ast_debug(4, "%s is unavailable because his device state is 'inuse'\n", member->membername); 00980 break; 00981 } 00982 case AST_DEVICE_UNKNOWN: 00983 if (conditions & QUEUE_EMPTY_UNKNOWN) { 00984 ast_debug(4, "%s is unavailable because his device state is 'unknown'\n", member->membername); 00985 break; 00986 } 00987 default: 00988 if (member->paused && (conditions & QUEUE_EMPTY_PAUSED)) { 00989 ast_debug(4, "%s is unavailable because he is paused'\n", member->membername); 00990 break; 00991 } else if ((conditions & QUEUE_EMPTY_WRAPUP) && member->lastcall && q->wrapuptime && (time(NULL) - q->wrapuptime < member->lastcall)) { 00992 ast_debug(4, "%s is unavailable because it has only been %d seconds since his last call (wrapup time is %d)\n", member->membername, (int) (time(NULL) - member->lastcall), q->wrapuptime); 00993 break; 00994 } else { 00995 ao2_unlock(q); 00996 ao2_ref(member, -1); 00997 ao2_iterator_destroy(&mem_iter); 00998 ast_debug(4, "%s is available.\n", member->membername); 00999 return 0; 01000 } 01001 break; 01002 } 01003 } 01004 ao2_iterator_destroy(&mem_iter); 01005 01006 ao2_unlock(q); 01007 return -1; 01008 }
| static char* handle_queue_add_member | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 6699 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.
06700 { 06701 char *queuename, *interface, *membername = NULL, *state_interface = NULL; 06702 int penalty; 06703 06704 switch ( cmd ) { 06705 case CLI_INIT: 06706 e->command = "queue add member"; 06707 e->usage = 06708 "Usage: queue add member <channel> to <queue> [[[penalty <penalty>] as <membername>] state_interface <interface>]\n" 06709 " Add a channel to a queue with optionally: a penalty, membername and a state_interface\n"; 06710 return NULL; 06711 case CLI_GENERATE: 06712 return complete_queue_add_member(a->line, a->word, a->pos, a->n); 06713 } 06714 06715 if ((a->argc != 6) && (a->argc != 8) && (a->argc != 10) && (a->argc != 12)) { 06716 return CLI_SHOWUSAGE; 06717 } else if (strcmp(a->argv[4], "to")) { 06718 return CLI_SHOWUSAGE; 06719 } else if ((a->argc >= 8) && strcmp(a->argv[6], "penalty")) { 06720 return CLI_SHOWUSAGE; 06721 } else if ((a->argc >= 10) && strcmp(a->argv[8], "as")) { 06722 return CLI_SHOWUSAGE; 06723 } else if ((a->argc == 12) && strcmp(a->argv[10], "state_interface")) { 06724 return CLI_SHOWUSAGE; 06725 } 06726 06727 queuename = a->argv[5]; 06728 interface = a->argv[3]; 06729 if (a->argc >= 8) { 06730 if (sscanf(a->argv[7], "%30d", &penalty) == 1) { 06731 if (penalty < 0) { 06732 ast_cli(a->fd, "Penalty must be >= 0\n"); 06733 penalty = 0; 06734 } 06735 } else { 06736 ast_cli(a->fd, "Penalty must be an integer >= 0\n"); 06737 penalty = 0; 06738 } 06739 } else { 06740 penalty = 0; 06741 } 06742 06743 if (a->argc >= 10) { 06744 membername = a->argv[9]; 06745 } 06746 06747 if (a->argc >= 12) { 06748 state_interface = a->argv[11]; 06749 } 06750 06751 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) { 06752 case RES_OKAY: 06753 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 06754 ast_cli(a->fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 06755 return CLI_SUCCESS; 06756 case RES_EXISTS: 06757 ast_cli(a->fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 06758 return CLI_FAILURE; 06759 case RES_NOSUCHQUEUE: 06760 ast_cli(a->fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 06761 return CLI_FAILURE; 06762 case RES_OUTOFMEMORY: 06763 ast_cli(a->fd, "Out of memory\n"); 06764 return CLI_FAILURE; 06765 case RES_NOT_DYNAMIC: 06766 ast_cli(a->fd, "Member not dynamic\n"); 06767 return CLI_FAILURE; 06768 default: 06769 return CLI_FAILURE; 06770 } 06771 }
| static char* handle_queue_pause_member | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 6883 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_strlen_zero(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_pause_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, RESULT_SUCCESS, set_member_paused(), ast_cli_entry::usage, and word.
06884 { 06885 char *queuename, *interface, *reason; 06886 int paused; 06887 06888 switch (cmd) { 06889 case CLI_INIT: 06890 e->command = "queue {pause|unpause} member"; 06891 e->usage = 06892 "Usage: queue {pause|unpause} member <member> [queue <queue> [reason <reason>]]\n" 06893 " Pause or unpause a queue member. Not specifying a particular queue\n" 06894 " will pause or unpause a member across all queues to which the member\n" 06895 " belongs.\n"; 06896 return NULL; 06897 case CLI_GENERATE: 06898 return complete_queue_pause_member(a->line, a-> word, a->pos, a->n); 06899 } 06900 06901 if (a->argc < 4 || a->argc == 5 || a->argc == 7 || a->argc > 8) { 06902 return CLI_SHOWUSAGE; 06903 } else if (a->argc >= 5 && strcmp(a->argv[4], "queue")) { 06904 return CLI_SHOWUSAGE; 06905 } else if (a->argc == 8 && strcmp(a->argv[6], "reason")) { 06906 return CLI_SHOWUSAGE; 06907 } 06908 06909 06910 interface = a->argv[3]; 06911 queuename = a->argc >= 6 ? a->argv[5] : NULL; 06912 reason = a->argc == 8 ? a->argv[7] : NULL; 06913 paused = !strcasecmp(a->argv[1], "pause"); 06914 06915 if (set_member_paused(queuename, interface, reason, paused) == RESULT_SUCCESS) { 06916 ast_cli(a->fd, "%spaused interface '%s'", paused ? "" : "un", interface); 06917 if (!ast_strlen_zero(queuename)) 06918 ast_cli(a->fd, " in queue '%s'", queuename); 06919 if (!ast_strlen_zero(reason)) 06920 ast_cli(a->fd, " for reason '%s'", reason); 06921 ast_cli(a->fd, "\n"); 06922 return CLI_SUCCESS; 06923 } else { 06924 ast_cli(a->fd, "Unable to %spause interface '%s'", paused ? "" : "un", interface); 06925 if (!ast_strlen_zero(queuename)) 06926 ast_cli(a->fd, " in queue '%s'", queuename); 06927 if (!ast_strlen_zero(reason)) 06928 ast_cli(a->fd, " for reason '%s'", reason); 06929 ast_cli(a->fd, "\n"); 06930 return CLI_FAILURE; 06931 } 06932 }
| static char* handle_queue_reload | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 7092 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, AST_FLAGS_ALL, ast_set_flag, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue(), ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, reload_handler(), ast_cli_entry::usage, and ast_cli_args::word.
07093 { 07094 struct ast_flags mask = {0,}; 07095 int i; 07096 07097 switch (cmd) { 07098 case CLI_INIT: 07099 e->command = "queue reload {parameters|members|rules|all}"; 07100 e->usage = 07101 "Usage: queue reload {parameters|members|rules|all} [<queuenames>]\n" 07102 "Reload queues. If <queuenames> are specified, only reload information pertaining\n" 07103 "to <queuenames>. One of 'parameters,' 'members,' 'rules,' or 'all' must be\n" 07104 "specified in order to know what information to reload. Below is an explanation\n" 07105 "of each of these qualifiers.\n" 07106 "\n" 07107 "\t'members' - reload queue members from queues.conf\n" 07108 "\t'parameters' - reload all queue options except for queue members\n" 07109 "\t'rules' - reload the queuerules.conf file\n" 07110 "\t'all' - reload queue rules, parameters, and members\n" 07111 "\n" 07112 "Note: the 'rules' qualifier here cannot actually be applied to a specific queue.\n" 07113 "Use of the 'rules' qualifier causes queuerules.conf to be reloaded. Even if only\n" 07114 "one queue is specified when using this command, reloading queue rules may cause\n" 07115 "other queues to be affected\n"; 07116 return NULL; 07117 case CLI_GENERATE: 07118 if (a->pos >= 3) { 07119 return complete_queue(a->line, a->word, a->pos, a->n); 07120 } else { 07121 return NULL; 07122 } 07123 } 07124 07125 if (a->argc < 3) 07126 return CLI_SHOWUSAGE; 07127 07128 if (!strcasecmp(a->argv[2], "rules")) { 07129 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 07130 } else if (!strcasecmp(a->argv[2], "members")) { 07131 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 07132 } else if (!strcasecmp(a->argv[2], "parameters")) { 07133 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 07134 } else if (!strcasecmp(a->argv[2], "all")) { 07135 ast_set_flag(&mask, AST_FLAGS_ALL); 07136 } 07137 07138 if (a->argc == 3) { 07139 reload_handler(1, &mask, NULL); 07140 return CLI_SUCCESS; 07141 } 07142 07143 for (i = 3; i < a->argc; ++i) { 07144 reload_handler(1, &mask, a->argv[i]); 07145 } 07146 07147 return CLI_SUCCESS; 07148 }
| static char* handle_queue_remove_member | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 6818 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, ast_cli(), ast_queue_log(), CLI_FAILURE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue_remove_member(), ast_cli_args::fd, ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, ast_cli_entry::usage, and ast_cli_args::word.
06819 { 06820 char *queuename, *interface; 06821 06822 switch (cmd) { 06823 case CLI_INIT: 06824 e->command = "queue remove member"; 06825 e->usage = 06826 "Usage: queue remove member <channel> from <queue>\n" 06827 " Remove a specific channel from a queue.\n"; 06828 return NULL; 06829 case CLI_GENERATE: 06830 return complete_queue_remove_member(a->line, a->word, a->pos, a->n); 06831 } 06832 06833 if (a->argc != 6) { 06834 return CLI_SHOWUSAGE; 06835 } else if (strcmp(a->argv[4], "from")) { 06836 return CLI_SHOWUSAGE; 06837 } 06838 06839 queuename = a->argv[5]; 06840 interface = a->argv[3]; 06841 06842 switch (remove_from_queue(queuename, interface)) { 06843 case RES_OKAY: 06844 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 06845 ast_cli(a->fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 06846 return CLI_SUCCESS; 06847 case RES_EXISTS: 06848 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 06849 return CLI_FAILURE; 06850 case RES_NOSUCHQUEUE: 06851 ast_cli(a->fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 06852 return CLI_FAILURE; 06853 case RES_OUTOFMEMORY: 06854 ast_cli(a->fd, "Out of memory\n"); 06855 return CLI_FAILURE; 06856 case RES_NOT_DYNAMIC: 06857 ast_cli(a->fd, "Unable to remove interface '%s' from queue '%s': Member is not dynamic\n", interface, queuename); 06858 return CLI_FAILURE; 06859 default: 06860 return CLI_FAILURE; 06861 } 06862 }
| static char* handle_queue_reset | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 7053 of file app_queue.c.
References ast_cli_args::argc, ast_cli_args::argv, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, complete_queue(), ast_cli_args::line, ast_cli_args::n, ast_cli_args::pos, QUEUE_RESET_STATS, reload_handler(), ast_cli_entry::usage, and ast_cli_args::word.
07054 { 07055 struct ast_flags mask = {QUEUE_RESET_STATS,}; 07056 int i; 07057 07058 switch (cmd) { 07059 case CLI_INIT: 07060 e->command = "queue reset stats"; 07061 e->usage = 07062 "Usage: queue reset stats [<queuenames>]\n" 07063 "\n" 07064 "Issuing this command will reset statistics for\n" 07065 "<queuenames>, or for all queues if no queue is\n" 07066 "specified.\n"; 07067 return NULL; 07068 case CLI_GENERATE: 07069 if (a->pos >= 3) { 07070 return complete_queue(a->line, a->word, a->pos, a->n); 07071 } else { 07072 return NULL; 07073 } 07074 } 07075 07076 if (a->argc < 3) { 07077 return CLI_SHOWUSAGE; 07078 } 07079 07080 if (a->argc == 3) { 07081 reload_handler(1, &mask, NULL); 07082 return CLI_SUCCESS; 07083 } 07084 07085 for (i = 3; i < a->argc; ++i) { 07086 reload_handler(1, &mask, a->argv[i]); 07087 } 07088 07089 return CLI_SUCCESS; 07090 }
| static char* handle_queue_rule_show | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 7019 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.
07020 { 07021 char *rule; 07022 struct rule_list *rl_iter; 07023 struct penalty_rule *pr_iter; 07024 switch (cmd) { 07025 case CLI_INIT: 07026 e->command = "queue show rules"; 07027 e->usage = 07028 "Usage: queue show rules [rulename]\n" 07029 " Show the list of rules associated with rulename. If no\n" 07030 " rulename is specified, list all rules defined in queuerules.conf\n"; 07031 return NULL; 07032 case CLI_GENERATE: 07033 return complete_queue_rule_show(a->line, a->word, a->pos, a->n); 07034 } 07035 07036 if (a->argc != 3 && a->argc != 4) 07037 return CLI_SHOWUSAGE; 07038 07039 rule = a->argc == 4 ? a->argv[3] : ""; 07040 AST_LIST_LOCK(&rule_lists); 07041 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 07042 if (ast_strlen_zero(rule) || !strcasecmp(rl_iter->name, rule)) { 07043 ast_cli(a->fd, "Rule: %s\n", rl_iter->name); 07044 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 07045 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); 07046 } 07047 } 07048 } 07049 AST_LIST_UNLOCK(&rule_lists); 07050 return CLI_SUCCESS; 07051 }
| static char* handle_queue_set_member_penalty | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 6957 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.
06958 { 06959 char *queuename = NULL, *interface; 06960 int penalty = 0; 06961 06962 switch (cmd) { 06963 case CLI_INIT: 06964 e->command = "queue set penalty"; 06965 e->usage = 06966 "Usage: queue set penalty <penalty> on <interface> [in <queue>]\n" 06967 " Set a member's penalty in the queue specified. If no queue is specified\n" 06968 " then that interface's penalty is set in all queues to which that interface is a member\n"; 06969 return NULL; 06970 case CLI_GENERATE: 06971 return complete_queue_set_member_penalty(a->line, a->word, a->pos, a->n); 06972 } 06973 06974 if (a->argc != 6 && a->argc != 8) { 06975 return CLI_SHOWUSAGE; 06976 } else if (strcmp(a->argv[4], "on") || (a->argc > 6 && strcmp(a->argv[6], "in"))) { 06977 return CLI_SHOWUSAGE; 06978 } 06979 06980 if (a->argc == 8) 06981 queuename = a->argv[7]; 06982 interface = a->argv[5]; 06983 penalty = atoi(a->argv[3]); 06984 06985 switch (set_member_penalty(queuename, interface, penalty)) { 06986 case RESULT_SUCCESS: 06987 ast_cli(a->fd, "Set penalty on interface '%s' from queue '%s'\n", interface, queuename); 06988 return CLI_SUCCESS; 06989 case RESULT_FAILURE: 06990 ast_cli(a->fd, "Failed to set penalty on interface '%s' from queue '%s'\n", interface, queuename); 06991 return CLI_FAILURE; 06992 default: 06993 return CLI_FAILURE; 06994 } 06995 }
| static int handle_statechange | ( | void * | datap | ) | [static] |
set a member's status based on device state of that member's interface
Definition at line 1046 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), ast_copy_string(), ast_debug, ast_devstate2str(), ast_free, statechange::dev, call_queue::found, call_queue::members, queue_t_unref, queues, statechange::state, member::state_interface, and update_status().
Referenced by device_state_cb().
01047 { 01048 struct statechange *sc = datap; 01049 struct ao2_iterator miter, qiter; 01050 struct member *m; 01051 struct call_queue *q; 01052 char interface[80], *slash_pos; 01053 int found = 0; 01054 01055 qiter = ao2_iterator_init(queues, 0); 01056 while ((q = ao2_t_iterator_next(&qiter, "Iterate over queues"))) { 01057 ao2_lock(q); 01058 01059 miter = ao2_iterator_init(q->members, 0); 01060 for (; (m = ao2_iterator_next(&miter)); ao2_ref(m, -1)) { 01061 ast_copy_string(interface, m->state_interface, sizeof(interface)); 01062 01063 if ((slash_pos = strchr(interface, '/'))) 01064 if (!strncasecmp(interface, "Local/", 6) && (slash_pos = strchr(slash_pos + 1, '/'))) 01065 *slash_pos = '\0'; 01066 01067 if (!strcasecmp(interface, sc->dev)) { 01068 found = 1; 01069 update_status(q, m, sc->state); 01070 ao2_ref(m, -1); 01071 break; 01072 } 01073 } 01074 ao2_iterator_destroy(&miter); 01075 01076 ao2_unlock(q); 01077 queue_t_unref(q, "Done with iterator"); 01078 } 01079 ao2_iterator_destroy(&qiter); 01080 01081 if (found) 01082 ast_debug(1, "Device '%s' changed to state '%d' (%s)\n", sc->dev, sc->state, ast_devstate2str(sc->state)); 01083 else 01084 ast_debug(3, "Device '%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", sc->dev, sc->state, ast_devstate2str(sc->state)); 01085 01086 ast_free(sc); 01087 return 0; 01088 }
| static void hangupcalls | ( | struct callattempt * | outgoing, | |
| struct ast_channel * | exception, | |||
| int | cancel_answered_elsewhere | |||
| ) | [static] |
Hang up a list of outgoing calls.
Definition at line 2300 of file app_queue.c.
References ao2_ref, AST_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_hangup(), ast_set_flag, callattempt::chan, callattempt::member, and callattempt::q_next.
Referenced by try_calling().
02301 { 02302 struct callattempt *oo; 02303 02304 while (outgoing) { 02305 /* If someone else answered the call we should indicate this in the CANCEL */ 02306 /* Hangup any existing lines we have open */ 02307 if (outgoing->chan && (outgoing->chan != exception)) { 02308 if (exception || cancel_answered_elsewhere) 02309 ast_set_flag(outgoing->chan, AST_FLAG_ANSWERED_ELSEWHERE); 02310 ast_hangup(outgoing->chan); 02311 } 02312 oo = outgoing; 02313 outgoing = outgoing->q_next; 02314 if (oo->member) 02315 ao2_ref(oo->member, -1); 02316 ast_free(oo); 02317 } 02318 }
| static void init_queue | ( | struct call_queue * | q | ) | [static] |
Initialize Queue default values.
Definition at line 1174 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::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_single_queue().
01175 { 01176 int i; 01177 struct penalty_rule *pr_iter; 01178 01179 q->dead = 0; 01180 q->retry = DEFAULT_RETRY; 01181 q->timeout = DEFAULT_TIMEOUT; 01182 q->maxlen = 0; 01183 q->announcefrequency = 0; 01184 q->minannouncefrequency = DEFAULT_MIN_ANNOUNCE_FREQUENCY; 01185 q->announceholdtime = 1; 01186 q->announcepositionlimit = 10; /* Default 10 positions */ 01187 q->announceposition = ANNOUNCEPOSITION_YES; /* Default yes */ 01188 q->roundingseconds = 0; /* Default - don't announce seconds */ 01189 q->servicelevel = 0; 01190 q->ringinuse = 1; 01191 q->setinterfacevar = 0; 01192 q->setqueuevar = 0; 01193 q->setqueueentryvar = 0; 01194 q->autofill = autofill_default; 01195 q->montype = montype_default; 01196 q->monfmt[0] = '\0'; 01197 q->reportholdtime = 0; 01198 q->wrapuptime = 0; 01199 q->joinempty = 0; 01200 q->leavewhenempty = 0; 01201 q->memberdelay = 0; 01202 q->maskmemberstatus = 0; 01203 q->eventwhencalled = 0; 01204 q->weight = 0; 01205 q->timeoutrestart = 0; 01206 q->periodicannouncefrequency = 0; 01207 q->randomperiodicannounce = 0; 01208 q->numperiodicannounce = 0; 01209 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01210 if (!q->members) { 01211 if (q->strategy == QUEUE_STRATEGY_LINEAR) 01212 /* linear strategy depends on order, so we have to place all members in a single bucket */ 01213 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); 01214 else 01215 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 01216 } 01217 q->found = 1; 01218 01219 ast_string_field_set(q, sound_next, "queue-youarenext"); 01220 ast_string_field_set(q, sound_thereare, "queue-thereare"); 01221 ast_string_field_set(q, sound_calls, "queue-callswaiting"); 01222 ast_string_field_set(q, queue_quantity1, "queue-quantity1"); 01223 ast_string_field_set(q, queue_quantity2, "queue-quantity2"); 01224 ast_string_field_set(q, sound_holdtime, "queue-holdtime"); 01225 ast_string_field_set(q, sound_minutes, "queue-minutes"); 01226 ast_string_field_set(q, sound_minute, "queue-minute"); 01227 ast_string_field_set(q, sound_seconds, "queue-seconds"); 01228 ast_string_field_set(q, sound_thanks, "queue-thankyou"); 01229 ast_string_field_set(q, sound_reporthold, "queue-reporthold"); 01230 01231 if ((q->sound_periodicannounce[0] = ast_str_create(32))) 01232 ast_str_set(&q->sound_periodicannounce[0], 0, "queue-periodic-announce"); 01233 01234 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 01235 if (q->sound_periodicannounce[i]) 01236 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", ""); 01237 } 01238 01239 while ((pr_iter = AST_LIST_REMOVE_HEAD(&q->rules,list))) 01240 ast_free(pr_iter); 01241 }
| 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 921 of file app_queue.c.
References call_queue::head, queue_ent::next, and queue_ref().
Referenced by join_queue().
00922 { 00923 struct queue_ent *cur; 00924 00925 if (!q || !new) 00926 return; 00927 if (prev) { 00928 cur = prev->next; 00929 prev->next = new; 00930 } else { 00931 cur = q->head; 00932 q->head = new; 00933 } 00934 new->next = cur; 00935 00936 /* every queue_ent must have a reference to it's parent call_queue, this 00937 * reference does not go away until the end of the queue_ent's life, meaning 00938 * that even when the queue_ent leaves the call_queue this ref must remain. */ 00939 queue_ref(q); 00940 new->parent = q; 00941 new->pos = ++(*pos); 00942 new->opos = *pos; 00943 }
| 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 1272 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_strlen_zero(), 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().
01273 { 01274 char *timestr, *maxstr, *minstr, *contentdup; 01275 struct penalty_rule *rule = NULL, *rule_iter; 01276 struct rule_list *rl_iter; 01277 int penaltychangetime, inserted = 0; 01278 01279 if (!(rule = ast_calloc(1, sizeof(*rule)))) { 01280 return -1; 01281 } 01282 01283 contentdup = ast_strdupa(content); 01284 01285 if (!(maxstr = strchr(contentdup, ','))) { 01286 ast_log(LOG_WARNING, "Improperly formatted penaltychange rule at line %d. Ignoring.\n", linenum); 01287 ast_free(rule); 01288 return -1; 01289 } 01290 01291 *maxstr++ = '\0'; 01292 timestr = contentdup; 01293 01294 if ((penaltychangetime = atoi(timestr)) < 0) { 01295 ast_log(LOG_WARNING, "Improper time parameter specified for penaltychange rule at line %d. Ignoring.\n", linenum); 01296 ast_free(rule); 01297 return -1; 01298 } 01299 01300 rule->time = penaltychangetime; 01301 01302 if ((minstr = strchr(maxstr,','))) 01303 *minstr++ = '\0'; 01304 01305 /* The last check will evaluate true if either no penalty change is indicated for a given rule 01306 * OR if a min penalty change is indicated but no max penalty change is */ 01307 if (*maxstr == '+' || *maxstr == '-' || *maxstr == '\0') { 01308 rule->max_relative = 1; 01309 } 01310 01311 rule->max_value = atoi(maxstr); 01312 01313 if (!ast_strlen_zero(minstr)) { 01314 if (*minstr == '+' || *minstr == '-') 01315 rule->min_relative = 1; 01316 rule->min_value = atoi(minstr); 01317 } else /*there was no minimum specified, so assume this means no change*/ 01318 rule->min_relative = 1; 01319 01320 /*We have the rule made, now we need to insert it where it belongs*/ 01321 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list){ 01322 if (strcasecmp(rl_iter->name, list_name)) 01323 continue; 01324 01325 AST_LIST_TRAVERSE_SAFE_BEGIN(&rl_iter->rules, rule_iter, list) { 01326 if (rule->time < rule_iter->time) { 01327 AST_LIST_INSERT_BEFORE_CURRENT(rule, list); 01328 inserted = 1; 01329 break; 01330 } 01331 } 01332 AST_LIST_TRAVERSE_SAFE_END; 01333 01334 if (!inserted) { 01335 AST_LIST_INSERT_TAIL(&rl_iter->rules, rule, list); 01336 } 01337 } 01338 01339 return 0; 01340 }
| static const char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 839 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().
00840 { 00841 int x; 00842 00843 for (x = 0; x < ARRAY_LEN(strategies); x++) { 00844 if (strategy == strategies[x].strategy) 00845 return strategies[x].name; 00846 } 00847 00848 return "<unknown>"; 00849 }
| static struct member* interface_exists | ( | struct call_queue * | q, | |
| const char * | interface | |||
| ) | [static, read] |
Definition at line 4330 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().
04331 { 04332 struct member *mem; 04333 struct ao2_iterator mem_iter; 04334 04335 if (!q) 04336 return NULL; 04337 04338 mem_iter = ao2_iterator_init(q->members, 0); 04339 while ((mem = ao2_iterator_next(&mem_iter))) { 04340 if (!strcasecmp(interface, mem->interface)) { 04341 ao2_iterator_destroy(&mem_iter); 04342 return mem; 04343 } 04344 ao2_ref(mem, -1); 04345 } 04346 ao2_iterator_destroy(&mem_iter); 04347 04348 return NULL; 04349 }
| 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 3120 of file app_queue.c.
References ao2_lock(), ao2_unlock(), ast_debug, call_queue::autofill, queue_ent::chan, call_queue::head, ast_channel::name, queue_ent::next, num_available_members(), queue_ent::parent, queue_ent::pending, and queue_ent::pos.
Referenced by queue_exec(), and wait_our_turn().
03121 { 03122 struct queue_ent *ch; 03123 int res; 03124 int avl; 03125 int idx = 0; 03126 /* This needs a lock. How many members are available to be served? */ 03127 ao2_lock(qe->parent); 03128 03129 avl = num_available_members(qe->parent); 03130 03131 ch = qe->parent->head; 03132 03133 ast_debug(1, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); 03134 03135 while ((idx < avl) && (ch) && (ch != qe)) { 03136 if (!ch->pending) 03137 idx++; 03138 ch = ch->next; 03139 } 03140 03141 ao2_unlock(qe->parent); 03142 /* If the queue entry is within avl [the number of available members] calls from the top ... 03143 * Autofill and position check added to support autofill=no (as only calls 03144 * from the front of the queue are valid when autofill is disabled) 03145 */ 03146 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) { 03147 ast_debug(1, "It's our turn (%s).\n", qe->chan->name); 03148 res = 1; 03149 } else { 03150 ast_debug(1, "It's not our turn (%s).\n", qe->chan->name); 03151 res = 0; 03152 } 03153 03154 return res; 03155 }
| static int join_queue | ( | char * | queuename, | |
| struct queue_ent * | qe, | |||
| enum queue_result * | reason | |||
| ) | [static] |
Definition at line 1956 of file app_queue.c.
References call_queue::announce, queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), ast_debug, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::context, queue_ent::context, call_queue::count, 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, ast_channel::name, queue_ent::next, queue_ent::pos, queue_ent::prio, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_UNKNOWN, queues, S_OR, status, and ast_channel::uniqueid.
Referenced by queue_exec().
01957 { 01958 struct call_queue *q; 01959 struct queue_ent *cur, *prev = NULL; 01960 int res = -1; 01961 int pos = 0; 01962 int inserted = 0; 01963 01964 if (!(q = load_realtime_queue(queuename))) 01965 return res; 01966 01967 ao2_lock(queues); 01968 ao2_lock(q); 01969 01970 /* This is our one */ 01971 if (q->joinempty) { 01972 int status = 0; 01973 if ((status = get_member_status(q, qe->max_penalty, qe->min_penalty, q->joinempty))) { 01974 *reason = QUEUE_JOINEMPTY; 01975 ao2_unlock(q); 01976 ao2_unlock(queues); 01977 return res; 01978 } 01979 } 01980 if (*reason == QUEUE_UNKNOWN && q->maxlen && (q->count >= q->maxlen)) 01981 *reason = QUEUE_FULL; 01982 else if (*reason == QUEUE_UNKNOWN) { 01983 /* There's space for us, put us at the right position inside 01984 * the queue. 01985 * Take into account the priority of the calling user */ 01986 inserted = 0; 01987 prev = NULL; 01988 cur = q->head; 01989 while (cur) { 01990 /* We have higher priority than the current user, enter 01991 * before him, after all the other users with priority 01992 * higher or equal to our priority. */ 01993 if ((!inserted) && (qe->prio > cur->prio)) { 01994 insert_entry(q, prev, qe, &pos); 01995 inserted = 1; 01996 } 01997 cur->pos = ++pos; 01998 prev = cur; 01999 cur = cur->next; 02000 } 02001 /* No luck, join at the end of the queue */ 02002 if (!inserted) 02003 insert_entry(q, prev, qe, &pos); 02004 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 02005 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 02006 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 02007 q->count++; 02008 res = 0; 02009 manager_event(EVENT_FLAG_CALL, "Join", 02010 "Channel: %s\r\nCallerIDNum: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n", 02011 qe->chan->name, 02012 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */ 02013 S_OR(qe->chan->cid.cid_name, "unknown"), 02014 q->name, qe->pos, q->count, qe->chan->uniqueid ); 02015 ast_debug(1, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 02016 } 02017 ao2_unlock(q); 02018 ao2_unlock(queues); 02019 02020 return res; 02021 }
| static int kill_dead_members | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 5803 of file app_queue.c.
References CMP_MATCH, member::delme, member::dynamic, call_queue::membercount, member::state_interface, and member::status.
Referenced by reload_single_queue().
05804 { 05805 struct member *member = obj; 05806 struct call_queue *q = arg; 05807 05808 if (!member->delme) { 05809 if (member->dynamic) { 05810 /* dynamic members were not counted toward the member count 05811 * when reloading members from queues.conf, so we do that here 05812 */ 05813 q->membercount++; 05814 } 05815 member->status = ast_device_state(member->state_interface); 05816 return 0; 05817 } else { 05818 q->membercount--; 05819 return CMP_MATCH; 05820 } 05821 }
| static int kill_dead_queues | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 5941 of file app_queue.c.
References ast_strlen_zero(), CMP_MATCH, call_queue::dead, and call_queue::name.
Referenced by reload_queues().
05942 { 05943 struct call_queue *q = obj; 05944 char *queuename = arg; 05945 if ((ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name)) && q->dead) { 05946 return CMP_MATCH; 05947 } else { 05948 return 0; 05949 } 05950 }
| 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 2243 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, ast_channel::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, ast_channel::uniqueid, and var.
Referenced by queue_exec(), try_calling(), and wait_our_turn().
02244 { 02245 struct call_queue *q; 02246 struct queue_ent *current, *prev = NULL; 02247 struct penalty_rule *pr_iter; 02248 int pos = 0; 02249 02250 if (!(q = qe->parent)) 02251 return; 02252 queue_t_ref(q, "Copy queue pointer from queue entry"); 02253 ao2_lock(q); 02254 02255 prev = NULL; 02256 for (current = q->head; current; current = current->next) { 02257 if (current == qe) { 02258 q->count--; 02259 02260 /* Take us out of the queue */ 02261 manager_event(EVENT_FLAG_CALL, "Leave", 02262 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", 02263 qe->chan->name, q->name, q->count, qe->chan->uniqueid); 02264 ast_debug(1, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 02265 /* Take us out of the queue */ 02266 if (prev) 02267 prev->next = current->next; 02268 else 02269 q->head = current->next; 02270 /* Free penalty rules */ 02271 while ((pr_iter = AST_LIST_REMOVE_HEAD(&qe->qe_rules, list))) 02272 ast_free(pr_iter); 02273 } else { 02274 /* Renumber the people after us in the queue based on a new count */ 02275 current->pos = ++pos; 02276 prev = current; 02277 } 02278 } 02279 ao2_unlock(q); 02280 02281 /*If the queue is a realtime queue, check to see if it's still defined in real time*/ 02282 if (q->realtime) { 02283 struct ast_variable *var; 02284 if (!(var = ast_load_realtime("queues", "name", q->name, SENTINEL))) { 02285 q->dead = 1; 02286 } else { 02287 ast_variables_destroy(var); 02288 } 02289 } 02290 02291 if (q->dead) { 02292 /* It's dead and nobody is in it, so kill it */ 02293 queues_t_unlink(queues, q, "Queue is now dead; remove it from the container"); 02294 } 02295 /* unref the explicit ref earlier in the function */ 02296 queue_t_unref(q, "Expire copied reference"); 02297 }
| static int load_module | ( | void | ) | [static] |
Definition at line 7220 of file app_queue.c.
References ao2_container_alloc, aqm_exec(), ARRAY_LEN, ast_add_extension2(), ast_cli_register_multiple(), ast_context_find_or_create(), ast_custom_function_register, AST_EVENT_DEVICE_STATE, AST_EVENT_IE_END, ast_event_subscribe(), AST_FLAGS_ALL, ast_free_ptr(), ast_log(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_realtime_require_field(), ast_register_application_xml, 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_reload(), manager_queue_reset(), manager_queue_rule_show(), manager_queues_show(), manager_queues_status(), manager_queues_summary(), manager_remove_queue_member(), MAX_QUEUE_BUCKETS, pqm_exec(), ql_exec(), queue_cmp_cb(), queue_exec(), queue_hash_cb(), queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queuevar_function, queuewaitingcount_function, reload_handler(), reload_queue_members(), RQ_INTEGER1, RQ_UINTEGER2, rqm_exec(), SENTINEL, and upqm_exec().
07221 { 07222 int res; 07223 struct ast_context *con; 07224 struct ast_flags mask = {AST_FLAGS_ALL, }; 07225 07226 queues = ao2_container_alloc(MAX_QUEUE_BUCKETS, queue_hash_cb, queue_cmp_cb); 07227 07228 use_weight = 0; 07229 07230 if (reload_handler(0, &mask, NULL)) 07231 return AST_MODULE_LOAD_DECLINE; 07232 07233 con = ast_context_find_or_create(NULL, NULL, "app_queue_gosub_virtual_context", "app_queue"); 07234 if (!con) 07235 ast_log(LOG_ERROR, "Queue virtual context 'app_queue_gosub_virtual_context' does not exist and unable to create\n"); 07236 else 07237 ast_add_extension2(con, 1, "s", 1, NULL, NULL, "NoOp", ast_strdup(""), ast_free_ptr, "app_queue"); 07238 07239 if (queue_persistent_members) 07240 reload_queue_members(); 07241 07242 ast_cli_register_multiple(cli_queue, ARRAY_LEN(cli_queue)); 07243 res = ast_register_application_xml(app, queue_exec); 07244 res |= ast_register_application_xml(app_aqm, aqm_exec); 07245 res |= ast_register_application_xml(app_rqm, rqm_exec); 07246 res |= ast_register_application_xml(app_pqm, pqm_exec); 07247 res |= ast_register_application_xml(app_upqm, upqm_exec); 07248 res |= ast_register_application_xml(app_ql, ql_exec); 07249 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); 07250 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); 07251 res |= ast_manager_register("QueueSummary", 0, manager_queues_summary, "Queue Summary"); 07252 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); 07253 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); 07254 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); 07255 res |= ast_manager_register("QueueLog", EVENT_FLAG_AGENT, manager_queue_log_custom, "Adds custom entry in queue_log"); 07256 res |= ast_manager_register("QueuePenalty", EVENT_FLAG_AGENT, manager_queue_member_penalty, "Set the penalty for a queue member"); 07257 res |= ast_manager_register("QueueRule", 0, manager_queue_rule_show, "Queue Rules"); 07258 res |= ast_manager_register("QueueReload", 0, manager_queue_reload, "Reload a queue, queues, or any sub-section of a queue or queues"); 07259 res |= ast_manager_register("QueueReset", 0, manager_queue_reset, "Reset queue statistics"); 07260 res |= ast_custom_function_register(&queuevar_function); 07261 res |= ast_custom_function_register(&queuemembercount_function); 07262 res |= ast_custom_function_register(&queuemembercount_dep); 07263 res |= ast_custom_function_register(&queuememberlist_function); 07264 res |= ast_custom_function_register(&queuewaitingcount_function); 07265 res |= ast_custom_function_register(&queuememberpenalty_function); 07266 07267 if (!(devicestate_tps = ast_taskprocessor_get("app_queue", 0))) { 07268 ast_log(LOG_WARNING, "devicestate taskprocessor reference failed - devicestate notifications will not occur\n"); 07269 } 07270 07271 if (!(device_state_sub = ast_event_subscribe(AST_EVENT_DEVICE_STATE, device_state_cb, NULL, AST_EVENT_IE_END))) { 07272 res = -1; 07273 } 07274 07275 ast_realtime_require_field("queue_members", "paused", RQ_INTEGER1, 1, "uniqueid", RQ_UINTEGER2, 5, SENTINEL); 07276 07277 return res ? AST_MODULE_LOAD_DECLINE : 0; 07278 }
| 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 1830 of file app_queue.c.
References ao2_lock(), ao2_t_find, ao2_unlock(), ast_atomic_fetchadd_int(), ast_config_destroy(), ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, OBJ_POINTER, queues, call_queue::realtime, SENTINEL, update_realtime_members(), and call_queue::weight.
Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_qac(), queue_function_qac_dep(), and reload_queue_members().
01831 { 01832 struct ast_variable *queue_vars; 01833 struct ast_config *member_config = NULL; 01834 struct call_queue *q = NULL, tmpq = { 01835 .name = queuename, 01836 }; 01837 int prev_weight = 0; 01838 01839 /* Find the queue in the in-core list first. */ 01840 q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Look for queue in memory first"); 01841 01842 if (!q || q->realtime) { 01843 /*! \note Load from realtime before taking the "queues" container lock, to avoid blocking all 01844 queue operations while waiting for the DB. 01845 01846 This will be two separate database transactions, so we might 01847 see queue parameters as they were before another process 01848 changed the queue and member list as it was after the change. 01849 Thus we might see an empty member list when a queue is 01850 deleted. In practise, this is unlikely to cause a problem. */ 01851 01852 queue_vars = ast_load_realtime("queues", "name", queuename, SENTINEL); 01853 if (queue_vars) { 01854 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, SENTINEL); 01855 if (!member_config) { 01856 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n"); 01857 ast_variables_destroy(queue_vars); 01858 return NULL; 01859 } 01860 } 01861 if (q) { 01862 prev_weight = q->weight ? 1 : 0; 01863 } 01864 01865 ao2_lock(queues); 01866 01867 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 01868 if (member_config) { 01869 ast_config_destroy(member_config); 01870 } 01871 if (queue_vars) { 01872 ast_variables_destroy(queue_vars); 01873 } 01874 /* update the use_weight value if the queue's has gained or lost a weight */ 01875 if (q) { 01876 if (!q->weight && prev_weight) { 01877 ast_atomic_fetchadd_int(&use_weight, -1); 01878 } 01879 if (q->weight && !prev_weight) { 01880 ast_atomic_fetchadd_int(&use_weight, +1); 01881 } 01882 } 01883 /* Other cases will end up with the proper value for use_weight */ 01884 ao2_unlock(queues); 01885 01886 } else { 01887 update_realtime_members(q); 01888 } 01889 return q; 01890 }
| static int manager_add_queue_member | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6466 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().
06467 { 06468 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface; 06469 int paused, penalty = 0; 06470 06471 queuename = astman_get_header(m, "Queue"); 06472 interface = astman_get_header(m, "Interface"); 06473 penalty_s = astman_get_header(m, "Penalty"); 06474 paused_s = astman_get_header(m, "Paused"); 06475 membername = astman_get_header(m, "MemberName"); 06476 state_interface = astman_get_header(m, "StateInterface"); 06477 06478 if (ast_strlen_zero(queuename)) { 06479 astman_send_error(s, m, "'Queue' not specified."); 06480 return 0; 06481 } 06482 06483 if (ast_strlen_zero(interface)) { 06484 astman_send_error(s, m, "'Interface' not specified."); 06485 return 0; 06486 } 06487 06488 if (ast_strlen_zero(penalty_s)) 06489 penalty = 0; 06490 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) 06491 penalty = 0; 06492 06493 if (ast_strlen_zero(paused_s)) 06494 paused = 0; 06495 else 06496 paused = abs(ast_true(paused_s)); 06497 06498 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) { 06499 case RES_OKAY: 06500 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 06501 astman_send_ack(s, m, "Added interface to queue"); 06502 break; 06503 case RES_EXISTS: 06504 astman_send_error(s, m, "Unable to add interface: Already there"); 06505 break; 06506 case RES_NOSUCHQUEUE: 06507 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 06508 break; 06509 case RES_OUTOFMEMORY: 06510 astman_send_error(s, m, "Out of memory"); 06511 break; 06512 } 06513 06514 return 0; 06515 }
| static int manager_pause_queue_member | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6551 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().
06552 { 06553 const char *queuename, *interface, *paused_s, *reason; 06554 int paused; 06555 06556 interface = astman_get_header(m, "Interface"); 06557 paused_s = astman_get_header(m, "Paused"); 06558 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 06559 reason = astman_get_header(m, "Reason"); /* Optional - Only used for logging purposes */ 06560 06561 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 06562 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 06563 return 0; 06564 } 06565 06566 paused = abs(ast_true(paused_s)); 06567 06568 if (set_member_paused(queuename, interface, reason, paused)) 06569 astman_send_error(s, m, "Interface not found"); 06570 else 06571 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 06572 return 0; 06573 }
| static int manager_queue_log_custom | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6575 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().
06576 { 06577 const char *queuename, *event, *message, *interface, *uniqueid; 06578 06579 queuename = astman_get_header(m, "Queue"); 06580 uniqueid = astman_get_header(m, "UniqueId"); 06581 interface = astman_get_header(m, "Interface"); 06582 event = astman_get_header(m, "Event"); 06583 message = astman_get_header(m, "Message"); 06584 06585 if (ast_strlen_zero(queuename) || ast_strlen_zero(event)) { 06586 astman_send_error(s, m, "Need 'Queue' and 'Event' parameters."); 06587 return 0; 06588 } 06589 06590 ast_queue_log(queuename, S_OR(uniqueid, "NONE"), interface, event, "%s", message); 06591 astman_send_ack(s, m, "Event added successfully"); 06592 06593 return 0; 06594 }
| static int manager_queue_member_penalty | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6674 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().
06675 { 06676 const char *queuename, *interface, *penalty_s; 06677 int penalty; 06678 06679 interface = astman_get_header(m, "Interface"); 06680 penalty_s = astman_get_header(m, "Penalty"); 06681 /* Optional - if not supplied, set the penalty value for the given Interface in all queues */ 06682 queuename = astman_get_header(m, "Queue"); 06683 06684 if (ast_strlen_zero(interface) || ast_strlen_zero(penalty_s)) { 06685 astman_send_error(s, m, "Need 'Interface' and 'Penalty' parameters."); 06686 return 0; 06687 } 06688 06689 penalty = atoi(penalty_s); 06690 06691 if (set_member_penalty((char *)queuename, (char *)interface, penalty)) 06692 astman_send_error(s, m, "Invalid interface, queuename or penalty"); 06693 else 06694 astman_send_ack(s, m, "Interface penalty set successfully"); 06695 06696 return 0; 06697 }
| static int manager_queue_reload | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6596 of file app_queue.c.
References AST_FLAGS_ALL, ast_set_flag, astman_get_header(), astman_send_ack(), astman_send_error(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, reload_handler(), and S_OR.
Referenced by load_module().
06597 { 06598 struct ast_flags mask = {0,}; 06599 const char *queuename = NULL; 06600 int header_found = 0; 06601 06602 queuename = astman_get_header(m, "Queue"); 06603 if (!strcasecmp(S_OR(astman_get_header(m, "Members"), ""), "yes")) { 06604 ast_set_flag(&mask, QUEUE_RELOAD_MEMBER); 06605 header_found = 1; 06606 } 06607 if (!strcasecmp(S_OR(astman_get_header(m, "Rules"), ""), "yes")) { 06608 ast_set_flag(&mask, QUEUE_RELOAD_RULES); 06609 header_found = 1; 06610 } 06611 if (!strcasecmp(S_OR(astman_get_header(m, "Parameters"), ""), "yes")) { 06612 ast_set_flag(&mask, QUEUE_RELOAD_PARAMETERS); 06613 header_found = 1; 06614 } 06615 06616 if (!header_found) { 06617 ast_set_flag(&mask, AST_FLAGS_ALL); 06618 } 06619 06620 if (!reload_handler(1, &mask, queuename)) { 06621 astman_send_ack(s, m, "Queue reloaded successfully"); 06622 } else { 06623 astman_send_error(s, m, "Error encountered while reloading queue"); 06624 } 06625 return 0; 06626 }
| static int manager_queue_reset | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6628 of file app_queue.c.
References astman_get_header(), astman_send_ack(), astman_send_error(), QUEUE_RESET_STATS, and reload_handler().
Referenced by load_module().
06629 { 06630 const char *queuename = NULL; 06631 struct ast_flags mask = {QUEUE_RESET_STATS,}; 06632 06633 queuename = astman_get_header(m, "Queue"); 06634 06635 if (!reload_handler(1, &mask, queuename)) { 06636 astman_send_ack(s, m, "Queue stats reset successfully"); 06637 } else { 06638 astman_send_error(s, m, "Error encountered while resetting queue stats"); 06639 } 06640 return 0; 06641 }
| static int manager_queue_rule_show | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6269 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().
06270 { 06271 const char *rule = astman_get_header(m, "Rule"); 06272 struct rule_list *rl_iter; 06273 struct penalty_rule *pr_iter; 06274 06275 AST_LIST_LOCK(&rule_lists); 06276 AST_LIST_TRAVERSE(&rule_lists, rl_iter, list) { 06277 if (ast_strlen_zero(rule) || !strcasecmp(rule, rl_iter->name)) { 06278 astman_append(s, "RuleList: %s\r\n", rl_iter->name); 06279 AST_LIST_TRAVERSE(&rl_iter->rules, pr_iter, list) { 06280 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 ); 06281 } 06282 if (!ast_strlen_zero(rule)) 06283 break; 06284 } 06285 } 06286 AST_LIST_UNLOCK(&rule_lists); 06287 06288 astman_append(s, "\r\n\r\n"); 06289 06290 return RESULT_SUCCESS; 06291 }
| static int manager_queues_show | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6259 of file app_queue.c.
References __queues_show(), astman_append(), and RESULT_SUCCESS.
Referenced by load_module().
06260 { 06261 char *a[] = { "queue", "show" }; 06262 06263 __queues_show(s, -1, 2, a); 06264 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 06265 06266 return RESULT_SUCCESS; 06267 }
| static int manager_queues_status | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Queue status info via AMI.
Definition at line 6369 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, ast_channel::name, 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, call_queue::talktime, ast_channel::uniqueid, and call_queue::weight.
Referenced by load_module().
06370 { 06371 time_t now; 06372 int pos; 06373 const char *id = astman_get_header(m,"ActionID"); 06374 const char *queuefilter = astman_get_header(m,"Queue"); 06375 const char *memberfilter = astman_get_header(m,"Member"); 06376 char idText[256] = ""; 06377 struct call_queue *q; 06378 struct queue_ent *qe; 06379 float sl = 0; 06380 struct member *mem; 06381 struct ao2_iterator queue_iter; 06382 struct ao2_iterator mem_iter; 06383 06384 astman_send_ack(s, m, "Queue status will follow"); 06385 time(&now); 06386 if (!ast_strlen_zero(id)) 06387 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 06388 06389 queue_iter = ao2_iterator_init(queues, 0); 06390 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06391 ao2_lock(q); 06392 06393 /* List queue properties */ 06394 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 06395 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 06396 astman_append(s, "Event: QueueParams\r\n" 06397 "Queue: %s\r\n" 06398 "Max: %d\r\n" 06399 "Strategy: %s\r\n" 06400 "Calls: %d\r\n" 06401 "Holdtime: %d\r\n" 06402 "TalkTime: %d\r\n" 06403 "Completed: %d\r\n" 06404 "Abandoned: %d\r\n" 06405 "ServiceLevel: %d\r\n" 06406 "ServicelevelPerf: %2.1f\r\n" 06407 "Weight: %d\r\n" 06408 "%s" 06409 "\r\n", 06410 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, 06411 q->callsabandoned, q->servicelevel, sl, q->weight, idText); 06412 /* List Queue Members */ 06413 mem_iter = ao2_iterator_init(q->members, 0); 06414 while ((mem = ao2_iterator_next(&mem_iter))) { 06415 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter) || !strcmp(mem->membername, memberfilter)) { 06416 astman_append(s, "Event: QueueMember\r\n" 06417 "Queue: %s\r\n" 06418 "Name: %s\r\n" 06419 "Location: %s\r\n" 06420 "Membership: %s\r\n" 06421 "Penalty: %d\r\n" 06422 "CallsTaken: %d\r\n" 06423 "LastCall: %d\r\n" 06424 "Status: %d\r\n" 06425 "Paused: %d\r\n" 06426 "%s" 06427 "\r\n", 06428 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 06429 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 06430 } 06431 ao2_ref(mem, -1); 06432 } 06433 ao2_iterator_destroy(&mem_iter); 06434 /* List Queue Entries */ 06435 pos = 1; 06436 for (qe = q->head; qe; qe = qe->next) { 06437 astman_append(s, "Event: QueueEntry\r\n" 06438 "Queue: %s\r\n" 06439 "Position: %d\r\n" 06440 "Channel: %s\r\n" 06441 "Uniqueid: %s\r\n" 06442 "CallerIDNum: %s\r\n" 06443 "CallerIDName: %s\r\n" 06444 "Wait: %ld\r\n" 06445 "%s" 06446 "\r\n", 06447 q->name, pos++, qe->chan->name, qe->chan->uniqueid, 06448 S_OR(qe->chan->cid.cid_num, "unknown"), 06449 S_OR(qe->chan->cid.cid_name, "unknown"), 06450 (long) (now - qe->start), idText); 06451 } 06452 } 06453 ao2_unlock(q); 06454 queue_t_unref(q, "Done with iterator"); 06455 } 06456 ao2_iterator_destroy(&queue_iter); 06457 06458 astman_append(s, 06459 "Event: QueueStatusComplete\r\n" 06460 "%s" 06461 "\r\n",idText); 06462 06463 return RESULT_SUCCESS; 06464 }
| static int manager_queues_summary | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Summary of queue info via the AMI.
Definition at line 6294 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, member::status, and call_queue::talktime.
Referenced by load_module().
06295 { 06296 time_t now; 06297 int qmemcount = 0; 06298 int qmemavail = 0; 06299 int qchancount = 0; 06300 int qlongestholdtime = 0; 06301 const char *id = astman_get_header(m, "ActionID"); 06302 const char *queuefilter = astman_get_header(m, "Queue"); 06303 char idText[256] = ""; 06304 struct call_queue *q; 06305 struct queue_ent *qe; 06306 struct member *mem; 06307 struct ao2_iterator queue_iter; 06308 struct ao2_iterator mem_iter; 06309 06310 astman_send_ack(s, m, "Queue summary will follow"); 06311 time(&now); 06312 if (!ast_strlen_zero(id)) 06313 snprintf(idText, 256, "ActionID: %s\r\n", id); 06314 queue_iter = ao2_iterator_init(queues, 0); 06315 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 06316 ao2_lock(q); 06317 06318 /* List queue properties */ 06319 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 06320 /* Reset the necessary local variables if no queuefilter is set*/ 06321 qmemcount = 0; 06322 qmemavail = 0; 06323 qchancount = 0; 06324 qlongestholdtime = 0; 06325 06326 /* List Queue Members */ 06327 mem_iter = ao2_iterator_init(q->members, 0); 06328 while ((mem = ao2_iterator_next(&mem_iter))) { 06329 if ((mem->status != AST_DEVICE_UNAVAILABLE) && (mem->status != AST_DEVICE_INVALID)) { 06330 ++qmemcount; 06331 if (((mem->status == AST_DEVICE_NOT_INUSE) || (mem->status == AST_DEVICE_UNKNOWN)) && !(mem->paused)) { 06332 ++qmemavail; 06333 } 06334 } 06335 ao2_ref(mem, -1); 06336 } 06337 ao2_iterator_destroy(&mem_iter); 06338 for (qe = q->head; qe; qe = qe->next) { 06339 if ((now - qe->start) > qlongestholdtime) { 06340 qlongestholdtime = now - qe->start; 06341 } 06342 ++qchancount; 06343 } 06344 astman_append(s, "Event: QueueSummary\r\n" 06345 "Queue: %s\r\n" 06346 "LoggedIn: %d\r\n" 06347 "Available: %d\r\n" 06348 "Callers: %d\r\n" 06349 "HoldTime: %d\r\n" 06350 "TalkTime: %d\r\n" 06351 "LongestHoldTime: %d\r\n" 06352 "%s" 06353 "\r\n", 06354 q->name, qmemcount, qmemavail, qchancount, q->holdtime, q->talktime, qlongestholdtime, idText); 06355 } 06356 ao2_unlock(q); 06357 queue_t_unref(q, "Done with iterator"); 06358 } 06359 ao2_iterator_destroy(&queue_iter); 06360 astman_append(s, 06361 "Event: QueueSummaryComplete\r\n" 06362 "%s" 06363 "\r\n", idText); 06364 06365 return RESULT_SUCCESS; 06366 }
| static int manager_remove_queue_member | ( | struct mansession * | s, | |
| const struct message * | m | |||
| ) | [static] |
Definition at line 6517 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().
06518 { 06519 const char *queuename, *interface; 06520 06521 queuename = astman_get_header(m, "Queue"); 06522 interface = astman_get_header(m, "Interface"); 06523 06524 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 06525 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 06526 return 0; 06527 } 06528 06529 switch (remove_from_queue(queuename, interface)) { 06530 case RES_OKAY: 06531 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 06532 astman_send_ack(s, m, "Removed interface from queue"); 06533 break; 06534 case RES_EXISTS: 06535 astman_send_error(s, m, "Unable to remove interface: Not there"); 06536 break; 06537 case RES_NOSUCHQUEUE: 06538 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 06539 break; 06540 case RES_OUTOFMEMORY: 06541 astman_send_error(s, m, "Out of memory"); 06542 break; 06543 case RES_NOT_DYNAMIC: 06544 astman_send_error(s, m, "Member not dynamic"); 06545 break; 06546 } 06547 06548 return 0; 06549 }
| static int mark_dead_and_unfound | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 5930 of file app_queue.c.
References ast_strlen_zero(), call_queue::dead, call_queue::found, call_queue::name, and call_queue::realtime.
Referenced by reload_queues().
05931 { 05932 struct call_queue *q = obj; 05933 char *queuename = arg; 05934 if (!q->realtime && (ast_strlen_zero(queuename) || !strcasecmp(queuename, q->name))) { 05935 q->dead = 1; 05936 q->found = 0; 05937 } 05938 return 0; 05939 }
| static int mark_member_dead | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 5794 of file app_queue.c.
References member::delme, and member::dynamic.
Referenced by reload_single_queue().
| static int member_cmp_fn | ( | void * | obj1, | |
| void * | obj2, | |||
| int | flags | |||
| ) | [static] |
Definition at line 1164 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 1152 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
01153 { 01154 const struct member *mem = obj; 01155 const char *chname = strchr(mem->interface, '/'); 01156 int ret = 0, i; 01157 if (!chname) 01158 chname = mem->interface; 01159 for (i = 0; i < 5 && chname[i]; i++) 01160 ret += compress_char(chname[i]) << (i * 6); 01161 return ret; 01162 }
| 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 2328 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().
02329 { 02330 struct member *mem; 02331 int avl = 0; 02332 struct ao2_iterator mem_iter; 02333 02334 mem_iter = ao2_iterator_init(q->members, 0); 02335 while ((mem = ao2_iterator_next(&mem_iter))) { 02336 switch (mem->status) { 02337 case AST_DEVICE_INUSE: 02338 if (!q->ringinuse) 02339 break; 02340 /* else fall through */ 02341 case AST_DEVICE_NOT_INUSE: 02342 case AST_DEVICE_UNKNOWN: 02343 if (!mem->paused) { 02344 avl++; 02345 } 02346 break; 02347 } 02348 ao2_ref(mem, -1); 02349 02350 /* If autofill is not enabled or if the queue's strategy is ringall, then 02351 * we really don't care about the number of available members so much as we 02352 * do that there is at least one available. 02353 * 02354 * In fact, we purposely will return from this function stating that only 02355 * one member is available if either of those conditions hold. That way, 02356 * functions which determine what action to take based on the number of available 02357 * members will operate properly. The reasoning is that even if multiple 02358 * members are available, only the head caller can actually be serviced. 02359 */ 02360 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { 02361 break; 02362 } 02363 } 02364 ao2_iterator_destroy(&mem_iter); 02365 02366 return avl; 02367 }
| static void parse_empty_options | ( | const char * | value, | |
| enum empty_conditions * | empty, | |||
| int | joinempty | |||
| ) | [static] |
Definition at line 1342 of file app_queue.c.
References ast_false(), ast_log(), ast_true(), LOG_WARNING, QUEUE_EMPTY_INUSE, QUEUE_EMPTY_INVALID, QUEUE_EMPTY_PAUSED, QUEUE_EMPTY_PENALTY, QUEUE_EMPTY_RINGING, QUEUE_EMPTY_UNAVAILABLE, QUEUE_EMPTY_UNKNOWN, and QUEUE_EMPTY_WRAPUP.
Referenced by queue_set_param().
01343 { 01344 char *value_copy = ast_strdupa(value); 01345 char *option = NULL; 01346 while ((option = strsep(&value_copy, ","))) { 01347 if (!strcasecmp(option, "paused")) { 01348 *empty |= QUEUE_EMPTY_PAUSED; 01349 } else if (!strcasecmp(option, "penalty")) { 01350 *empty |= QUEUE_EMPTY_PENALTY; 01351 } else if (!strcasecmp(option, "inuse")) { 01352 *empty |= QUEUE_EMPTY_INUSE; 01353 } else if (!strcasecmp(option, "ringing")) { 01354 *empty |= QUEUE_EMPTY_RINGING; 01355 } else if (!strcasecmp(option, "invalid")) { 01356 *empty |= QUEUE_EMPTY_INVALID; 01357 } else if (!strcasecmp(option, "wrapup")) { 01358 *empty |= QUEUE_EMPTY_WRAPUP; 01359 } else if (!strcasecmp(option, "unavailable")) { 01360 *empty |= QUEUE_EMPTY_UNAVAILABLE; 01361 } else if (!strcasecmp(option, "unknown")) { 01362 *empty |= QUEUE_EMPTY_UNKNOWN; 01363 } else if (!strcasecmp(option, "loose")) { 01364 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID); 01365 } else if (!strcasecmp(option, "strict")) { 01366 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED | QUEUE_EMPTY_UNAVAILABLE); 01367 } else if ((ast_false(option) && joinempty) || (ast_true(option) && !joinempty)) { 01368 *empty = (QUEUE_EMPTY_PENALTY | QUEUE_EMPTY_INVALID | QUEUE_EMPTY_PAUSED); 01369 } else if ((ast_false(option) && !joinempty) || (ast_true(option) && joinempty)) { 01370 *empty = 0; 01371 } else { 01372 ast_log(LOG_WARNING, "Unknown option %s for '%s'\n", option, joinempty ? "joinempty" : "leavewhenempty"); 01373 } 01374 } 01375 }
| static int play_file | ( | struct ast_channel * | chan, | |
| const char * | filename | |||
| ) | [static] |
Definition at line 2023 of file app_queue.c.
References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), and ast_channel::language.
Referenced by say_periodic_announcement(), say_position(), and try_calling().
02024 { 02025 int res; 02026 02027 if (ast_strlen_zero(filename)) { 02028 return 0; 02029 } 02030 02031 ast_stopstream(chan); 02032 02033 res = ast_streamfile(chan, filename, chan->language); 02034 if (!res) 02035 res = ast_waitstream(chan, AST_DIGIT_ANY); 02036 02037 ast_stopstream(chan); 02038 02039 return res; 02040 }
| static int pqm_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
PauseQueueMember application.
Definition at line 4764 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
04765 { 04766 char *parse; 04767 AST_DECLARE_APP_ARGS(args, 04768 AST_APP_ARG(queuename); 04769 AST_APP_ARG(interface); 04770 AST_APP_ARG(options); 04771 AST_APP_ARG(reason); 04772 ); 04773 04774 if (ast_strlen_zero(data)) { 04775 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename],interface[,options][,reason])\n"); 04776 return -1; 04777 } 04778 04779 parse = ast_strdupa(data); 04780 04781 AST_STANDARD_APP_ARGS(args, parse); 04782 04783 if (ast_strlen_zero(args.interface)) { 04784 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 04785 return -1; 04786 } 04787 04788 if (set_member_paused(args.queuename, args.interface, args.reason, 1)) { 04789 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 04790 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 04791 return 0; 04792 } 04793 04794 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 04795 04796 return 0; 04797 }
| static int ql_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
QueueLog application.
Definition at line 4956 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, and parse().
Referenced by load_module().
04957 { 04958 char *parse; 04959 04960 AST_DECLARE_APP_ARGS(args, 04961 AST_APP_ARG(queuename); 04962 AST_APP_ARG(uniqueid); 04963 AST_APP_ARG(membername); 04964 AST_APP_ARG(event); 04965 AST_APP_ARG(params); 04966 ); 04967 04968 if (ast_strlen_zero(data)) { 04969 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo]\n"); 04970 return -1; 04971 } 04972 04973 parse = ast_strdupa(data); 04974 04975 AST_STANDARD_APP_ARGS(args, parse); 04976 04977 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 04978 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 04979 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename,uniqueid,membername,event[,additionalinfo])\n"); 04980 return -1; 04981 } 04982 04983 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 04984 "%s", args.params ? args.params : ""); 04985 04986 return 0; 04987 }
| static int queue_cmp_cb | ( | void * | obj, | |
| void * | arg, | |||
| int | flags | |||
| ) | [static] |
Definition at line 870 of file app_queue.c.
References CMP_MATCH, CMP_STOP, and call_queue::name.
Referenced by load_module().
00871 { 00872 struct call_queue *q = obj, *q2 = arg; 00873 return !strcasecmp(q->name, q2->name) ? CMP_MATCH | CMP_STOP : 0; 00874 }
| 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 5031 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_LIST_FIRST, ast_log(), ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strlen_zero(), ast_verb, ast_channel::cdr, queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, copy_rules(), 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, ast_channel::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_ent::qe_rules, QUEUE_CONTINUE, QUEUE_LEAVEEMPTY, 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(), ast_channel::uniqueid, update_qe_rule(), update_realtime_members(), url, queue_ent::valid_digits, wait_a_bit(), and wait_our_turn().
Referenced by load_module().
05032 { 05033 int res=-1; 05034 int ringing=0; 05035 const char *user_priority; 05036 const char *max_penalty_str; 05037 const char *min_penalty_str; 05038 int prio; 05039 int qcontinue = 0; 05040 int max_penalty, min_penalty; 05041 enum queue_result reason = QUEUE_UNKNOWN; 05042 /* whether to exit Queue application after the timeout hits */ 05043 int tries = 0; 05044 int noption = 0; 05045 char *parse; 05046 int makeannouncement = 0; 05047 AST_DECLARE_APP_ARGS(args, 05048 AST_APP_ARG(queuename); 05049 AST_APP_ARG(options); 05050 AST_APP_ARG(url); 05051 AST_APP_ARG(announceoverride); 05052 AST_APP_ARG(queuetimeoutstr); 05053 AST_APP_ARG(agi); 05054 AST_APP_ARG(macro); 05055 AST_APP_ARG(gosub); 05056 AST_APP_ARG(rule); 05057 ); 05058 /* Our queue entry */ 05059 struct queue_ent qe = { 0 }; 05060 05061 if (ast_strlen_zero(data)) { 05062 ast_log(LOG_WARNING, "Queue requires an argument: queuename[,options[,URL[,announceoverride[,timeout[,agi[,macro[,gosub[,rule]]]]]]]]\n"); 05063 return -1; 05064 } 05065 05066 parse = ast_strdupa(data); 05067 AST_STANDARD_APP_ARGS(args, parse); 05068 05069 /* Setup our queue entry */ 05070 qe.start = time(NULL); 05071 05072 /* set the expire time based on the supplied timeout; */ 05073 if (!ast_strlen_zero(args.queuetimeoutstr)) 05074 qe.expire = qe.start + atoi(args.queuetimeoutstr); 05075 else 05076 qe.expire = 0; 05077 05078 /* Get the priority from the variable ${QUEUE_PRIO} */ 05079 ast_channel_lock(chan); 05080 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 05081 if (user_priority) { 05082 if (sscanf(user_priority, "%30d", &prio) == 1) { 05083 ast_debug(1, "%s: Got priority %d from ${QUEUE_PRIO}.\n", chan->name, prio); 05084 } else { 05085 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 05086 user_priority, chan->name); 05087 prio = 0; 05088 } 05089 } else { 05090 ast_debug(3, "NO QUEUE_PRIO variable found. Using default.\n"); 05091 prio = 0; 05092 } 05093 05094 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 05095 05096 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 05097 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) { 05098 ast_debug(1, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", chan->name, max_penalty); 05099 } else { 05100 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 05101 max_penalty_str, chan->name); 05102 max_penalty = 0; 05103 } 05104 } else { 05105 max_penalty = 0; 05106 } 05107 05108 if ((min_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MIN_PENALTY"))) { 05109 if (sscanf(min_penalty_str, "%30d", &min_penalty) == 1) { 05110 ast_debug(1, "%s: Got min penalty %d from ${QUEUE_MIN_PENALTY}.\n", chan->name, min_penalty); 05111 } else { 05112 ast_log(LOG_WARNING, "${QUEUE_MIN_PENALTY}: Invalid value (%s), channel %s.\n", 05113 min_penalty_str, chan->name); 05114 min_penalty = 0; 05115 } 05116 } else { 05117 min_penalty = 0; 05118 } 05119 ast_channel_unlock(chan); 05120 05121 if (args.options && (strchr(args.options, 'r'))) 05122 ringing = 1; 05123 05124 if (args.options && (strchr(args.options, 'c'))) 05125 qcontinue = 1; 05126 05127 ast_debug(1, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 05128 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 05129 05130 qe.chan = chan; 05131 qe.prio = prio; 05132 qe.max_penalty = max_penalty; 05133 qe.min_penalty = min_penalty; 05134 qe.last_pos_said = 0; 05135 qe.last_pos = 0; 05136 qe.last_periodic_announce_time = time(NULL); 05137 qe.last_periodic_announce_sound = 0; 05138 qe.valid_digits = 0; 05139 if (join_queue(args.queuename, &qe, &reason)) { 05140 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 05141 set_queue_result(chan, reason); 05142 return 0; 05143 } 05144 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), 05145 S_OR(chan->cid.cid_num, "")); 05146 copy_rules(&qe, args.rule); 05147 qe.pr = AST_LIST_FIRST(&qe.qe_rules); 05148 check_turns: 05149 if (ringing) { 05150 ast_indicate(chan, AST_CONTROL_RINGING); 05151 } else { 05152 ast_moh_start(chan, qe.moh, NULL); 05153 } 05154 05155 /* This is the wait loop for callers 2 through maxlen */ 05156 res = wait_our_turn(&qe, ringing, &reason); 05157 if (res) { 05158 goto stop; 05159 } 05160 05161 makeannouncement = 0; 05162 05163 for (;;) { 05164 /* This is the wait loop for the head caller*/ 05165 /* To exit, they may get their call answered; */ 05166 /* they may dial a digit from the queue context; */ 05167 /* or, they may timeout. */ 05168 05169 /* Leave if we have exceeded our queuetimeout */ 05170 if (qe.expire && (time(NULL) >= qe.expire)) { 05171 record_abandoned(&qe); 05172 ast_cdr_noanswer(qe.chan->cdr); 05173 reason = QUEUE_TIMEOUT; 05174 res = 0; 05175 ast_queue_log(args.queuename, chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", 05176 qe.pos, qe.opos, (long) time(NULL) - qe.start); 05177 break; 05178 } 05179 05180 if (makeannouncement) { 05181 /* Make a position announcement, if enabled */ 05182 if (qe.parent->announcefrequency) 05183 if ((res = say_position(&qe,ringing))) 05184 goto stop; 05185 } 05186 makeannouncement = 1; 05187 05188 /* Make a periodic announcement, if enabled */ 05189 if (qe.parent->periodicannouncefrequency) 05190 if ((res = say_periodic_announcement(&qe,ringing))) 05191 goto stop; 05192 05193 /* Leave if we have exceeded our queuetimeout */ 05194 if (qe.expire && (time(NULL) >= qe.expire)) { 05195 record_abandoned(&qe); 05196 ast_cdr_noanswer(qe.chan->cdr); 05197 reason = QUEUE_TIMEOUT; 05198 res = 0; 05199 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 05200 break; 05201 } 05202 05203 /* see if we need to move to the next penalty level for this queue */ 05204 while (qe.pr && ((time(NULL) - qe.start) > qe.pr->time)) { 05205 update_qe_rule(&qe); 05206 } 05207 05208 /* Try calling all queue members for 'timeout' seconds */ 05209 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi, args.macro, args.gosub, ringing); 05210 if (res) { 05211 goto stop; 05212 } 05213 05214 if (qe.parent->leavewhenempty) { 05215 int status = 0; 05216 if ((status = get_member_status(qe.parent, qe.max_penalty, qe.min_penalty, qe.parent->leavewhenempty))) { 05217 record_abandoned(&qe); 05218 ast_cdr_noanswer(qe.chan->cdr); 05219 reason = QUEUE_LEAVEEMPTY; 05220 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 05221 res = 0; 05222 break; 05223 } 05224 } 05225 05226 /* exit after 'timeout' cycle if 'n' option enabled */ 05227 if (noption && tries >= qe.parent->membercount) { 05228 ast_verb(3, "Exiting on time-out cycle\n"); 05229 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 05230 record_abandoned(&qe); 05231 ast_cdr_noanswer(qe.chan->cdr); 05232 reason = QUEUE_TIMEOUT; 05233 res = 0; 05234 break; 05235 } 05236 05237 05238 /* Leave if we have exceeded our queuetimeout */ 05239 if (qe.expire && (time(NULL) >= qe.expire)) { 05240 record_abandoned(&qe); 05241 ast_cdr_noanswer(qe.chan->cdr); 05242 reason = QUEUE_TIMEOUT; 05243 res = 0; 05244 ast_queue_log(qe.parent->name, qe.chan->uniqueid,"NONE", "EXITWITHTIMEOUT", "%d|%d|%ld", qe.pos, qe.opos, (long) time(NULL) - qe.start); 05245 break; 05246 } 05247 05248 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 05249 update_realtime_members(qe.parent); 05250 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 05251 res = wait_a_bit(&qe); 05252 if (res) 05253 goto stop; 05254 05255 /* Since this is a priority queue and 05256 * it is not sure that we are still at the head 05257 * of the queue, go and check for our turn again. 05258 */ 05259 if (!is_our_turn(&qe)) { 05260 ast_debug(1, "Darn priorities, going back in queue (%s)!\n", qe.chan->name); 05261 goto check_turns; 05262 } 05263 } 05264 05265 stop: 05266 if (res) { 05267 if (res < 0) { 05268 if (!qe.handled) { 05269 record_abandoned(&qe); 05270 ast_cdr_noanswer(qe.chan->cdr); 05271 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 05272 "%d|%d|%ld", qe.pos, qe.opos, 05273 (long) time(NULL) - qe.start); 05274 res = -1; 05275 } else if (qcontinue) { 05276 reason = QUEUE_CONTINUE; 05277 res = 0; 05278 } 05279 } else if (qe.valid_digits) { 05280 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 05281 "%s|%d", qe.digits, qe.pos); 05282 } 05283 } 05284 05285 /* Don't allow return code > 0 */ 05286 if (res >= 0) { 05287 res = 0; 05288 if (ringing) { 05289 ast_indicate(chan, -1); 05290 } else { 05291 ast_moh_stop(chan); 05292 } 05293 ast_stopstream(chan); 05294 } 05295 05296 set_queue_variables(qe.parent, qe.chan); 05297 05298 leave_queue(&qe); 05299 if (reason != QUEUE_UNKNOWN) 05300 set_queue_result(chan, reason); 05301 05302 if (qe.parent) { 05303 /* every queue_ent is given a reference to it's parent call_queue when it joins the queue. 05304 * This ref must be taken away right before the queue_ent is destroyed. In this case 05305 * the queue_ent is about to be returned on the stack */ 05306 qe.parent = queue_unref(qe.parent); 05307 } 05308 05309 return res; 05310 }
| 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 5546 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.
05547 { 05548 int penalty; 05549 AST_DECLARE_APP_ARGS(args, 05550 AST_APP_ARG(queuename); 05551 AST_APP_ARG(interface); 05552 ); 05553 /* Make sure the returned value on error is NULL. */ 05554 buf[0] = '\0'; 05555 05556 if (ast_strlen_zero(data)) { 05557 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05558 return -1; 05559 } 05560 05561 AST_STANDARD_APP_ARGS(args, data); 05562 05563 if (args.argc < 2) { 05564 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05565 return -1; 05566 } 05567 05568 penalty = get_member_penalty (args.queuename, args.interface); 05569 05570 if (penalty >= 0) /* remember that buf is already '\0' */ 05571 snprintf (buf, len, "%d", penalty); 05572 05573 return 0; 05574 }
| 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 5577 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().
05578 { 05579 int penalty; 05580 AST_DECLARE_APP_ARGS(args, 05581 AST_APP_ARG(queuename); 05582 AST_APP_ARG(interface); 05583 ); 05584 05585 if (ast_strlen_zero(data)) { 05586 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05587 return -1; 05588 } 05589 05590 AST_STANDARD_APP_ARGS(args, data); 05591 05592 if (args.argc < 2) { 05593 ast_log(LOG_ERROR, "Missing argument. QUEUE_MEMBER_PENALTY(<queuename>,<interface>)\n"); 05594 return -1; 05595 } 05596 05597 penalty = atoi(value); 05598 05599 if (ast_strlen_zero(args.interface)) { 05600 ast_log (LOG_ERROR, "<interface> parameter can't be null\n"); 05601 return -1; 05602 } 05603 05604 /* if queuename = NULL then penalty will be set for interface in all the queues. */ 05605 if (set_member_penalty(args.queuename, args.interface, penalty)) { 05606 ast_log(LOG_ERROR, "Invalid interface, queue or penalty\n"); 05607 return -1; 05608 } 05609 05610 return 0; 05611 }
| 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 5365 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.
05366 { 05367 int count = 0; 05368 struct member *m; 05369 struct ao2_iterator mem_iter; 05370 struct call_queue *q; 05371 char *option; 05372 05373 if (ast_strlen_zero(data)) { 05374 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05375 return -1; 05376 } 05377 05378 if ((option = strchr(data, ','))) 05379 *option++ = '\0'; 05380 else 05381 option = "logged"; 05382 if ((q = load_realtime_queue(data))) { 05383 ao2_lock(q); 05384 if (!strcasecmp(option, "logged")) { 05385 mem_iter = ao2_iterator_init(q->members, 0); 05386 while ((m = ao2_iterator_next(&mem_iter))) { 05387 /* Count the agents who are logged in and presently answering calls */ 05388 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 05389 count++; 05390 } 05391 ao2_ref(m, -1); 05392 } 05393 ao2_iterator_destroy(&mem_iter); 05394 } else if (!strcasecmp(option, "free")) { 05395 mem_iter = ao2_iterator_init(q->members, 0); 05396 while ((m = ao2_iterator_next(&mem_iter))) { 05397 /* Count the agents who are logged in and presently answering calls */ 05398 if ((m->status == AST_DEVICE_NOT_INUSE) && (!m->paused)) { 05399 count++; 05400 } 05401 ao2_ref(m, -1); 05402 } 05403 ao2_iterator_destroy(&mem_iter); 05404 } else /* must be "count" */ 05405 count = q->membercount; 05406 ao2_unlock(q); 05407 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER()"); 05408 } else 05409 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05410 05411 snprintf(buf, len, "%d", count); 05412 05413 return 0; 05414 }
| 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 5421 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.
05422 { 05423 int count = 0; 05424 struct member *m; 05425 struct call_queue *q; 05426 struct ao2_iterator mem_iter; 05427 static int depflag = 1; 05428 05429 if (depflag) { 05430 depflag = 0; 05431 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"); 05432 } 05433 05434 if (ast_strlen_zero(data)) { 05435 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05436 return -1; 05437 } 05438 05439 if ((q = load_realtime_queue(data))) { 05440 ao2_lock(q); 05441 mem_iter = ao2_iterator_init(q->members, 0); 05442 while ((m = ao2_iterator_next(&mem_iter))) { 05443 /* Count the agents who are logged in and presently answering calls */ 05444 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 05445 count++; 05446 } 05447 ao2_ref(m, -1); 05448 } 05449 ao2_iterator_destroy(&mem_iter); 05450 ao2_unlock(q); 05451 queue_t_unref(q, "Done with temporary reference in QUEUE_MEMBER_COUNT"); 05452 } else 05453 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05454 05455 snprintf(buf, len, "%d", count); 05456 05457 return 0; 05458 }
| 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 5497 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.
05498 { 05499 struct call_queue *q, tmpq = { 05500 .name = data, 05501 }; 05502 struct member *m; 05503 05504 /* Ensure an otherwise empty list doesn't return garbage */ 05505 buf[0] = '\0'; 05506 05507 if (ast_strlen_zero(data)) { 05508 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 05509 return -1; 05510 } 05511 05512 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_MEMBER_LIST()"))) { 05513 int buflen = 0, count = 0; 05514 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 05515 05516 ao2_lock(q); 05517 while ((m = ao2_iterator_next(&mem_iter))) { 05518 /* strcat() is always faster than printf() */ 05519 if (count++) { 05520 strncat(buf + buflen, ",", len - buflen - 1); 05521 buflen++; 05522 } 05523 strncat(buf + buflen, m->interface, len - buflen - 1); 05524 buflen += strlen(m->interface); 05525 /* Safeguard against overflow (negative length) */ 05526 if (buflen >= len - 2) { 05527 ao2_ref(m, -1); 05528 ast_log(LOG_WARNING, "Truncating list\n"); 05529 break; 05530 } 05531 ao2_ref(m, -1); 05532 } 05533 ao2_iterator_destroy(&mem_iter); 05534 ao2_unlock(q); 05535 queue_t_unref(q, "Done with QUEUE_MEMBER_LIST()"); 05536 } else 05537 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05538 05539 /* We should already be terminated, but let's make sure. */ 05540 buf[len - 1] = '\0'; 05541 05542 return 0; 05543 }
| 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 5461 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.
05462 { 05463 int count = 0; 05464 struct call_queue *q, tmpq = { 05465 .name = data, 05466 }; 05467 struct ast_variable *var = NULL; 05468 05469 buf[0] = '\0'; 05470 05471 if (ast_strlen_zero(data)) { 05472 ast_log(LOG_ERROR, "QUEUE_WAITING_COUNT requires an argument: queuename\n"); 05473 return -1; 05474 } 05475 05476 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE_WAITING_COUNT()"))) { 05477 ao2_lock(q); 05478 count = q->count; 05479 ao2_unlock(q); 05480 queue_t_unref(q, "Done with reference in QUEUE_WAITING_COUNT()"); 05481 } else if ((var = ast_load_realtime("queues", "name", data, SENTINEL))) { 05482 /* if the queue is realtime but was not found in memory, this 05483 * means that the queue had been deleted from memory since it was 05484 * "dead." This means it has a 0 waiting count 05485 */ 05486 count = 0; 05487 ast_variables_destroy(var); 05488 } else 05489 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05490 05491 snprintf(buf, len, "%d", count); 05492 05493 return 0; 05494 }
| 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 5317 of file app_queue.c.
References ao2_lock(), ao2_t_find, ao2_unlock(), ast_log(), ast_strlen_zero(), call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::count, call_queue::holdtime, int2strat(), LOG_ERROR, LOG_WARNING, call_queue::maxlen, OBJ_POINTER, pbx_builtin_setvar_multiple(), queue_t_unref, queues, call_queue::servicelevel, call_queue::setqueuevar, call_queue::strategy, and call_queue::talktime.
05318 { 05319 int res = -1; 05320 struct call_queue *q, tmpq = { 05321 .name = data, 05322 }; 05323 05324 char interfacevar[256] = ""; 05325 float sl = 0; 05326 05327 if (ast_strlen_zero(data)) { 05328 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 05329 return -1; 05330 } 05331 05332 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find for QUEUE() function"))) { 05333 ao2_lock(q); 05334 if (q->setqueuevar) { 05335 sl = 0; 05336 res = 0; 05337 05338 if (q->callscompleted > 0) { 05339 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 05340 } 05341 05342 snprintf(interfacevar, sizeof(interfacevar), 05343 "QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 05344 q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 05345 05346 pbx_builtin_setvar_multiple(chan, interfacevar); 05347 } 05348 05349 ao2_unlock(q); 05350 queue_t_unref(q, "Done with QUEUE() function"); 05351 } else { 05352 ast_log(LOG_WARNING, "queue %s was not found\n", data); 05353 } 05354 05355 snprintf(buf, len, "%d", res); 05356 05357 return 0; 05358 }
| static int queue_hash_cb | ( | const void * | obj, | |
| const int | flags | |||
| ) | [static] |
Definition at line 863 of file app_queue.c.
References ast_str_case_hash(), and call_queue::name.
Referenced by load_module().
00864 { 00865 const struct call_queue *q = obj; 00866 00867 return ast_str_case_hash(q->name); 00868 }
| static struct call_queue* queue_ref | ( | struct call_queue * | q | ) | [static, read] |
Definition at line 888 of file app_queue.c.
References ao2_ref.
Referenced by insert_entry().
00889 { 00890 ao2_ref(q, 1); 00891 return q; 00892 }
| static void queue_set_global_params | ( | struct ast_config * | cfg | ) | [static] |
Set the global queue parameters as defined in the "general" section of queues.conf
Definition at line 5698 of file app_queue.c.
References ast_true(), and ast_variable_retrieve().
Referenced by reload_queues().
05699 { 05700 const char *general_val = NULL; 05701 queue_persistent_members = 0; 05702 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 05703 queue_persistent_members = ast_true(general_val); 05704 autofill_default = 0; 05705 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 05706 autofill_default = ast_true(general_val); 05707 montype_default = 0; 05708 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) { 05709 if (!strcasecmp(general_val, "mixmonitor")) 05710 montype_default = 1; 05711 } 05712 update_cdr = 0; 05713 if ((general_val = ast_variable_retrieve(cfg, "general", "updatecdr"))) 05714 update_cdr = ast_true(general_val); 05715 shared_lastcall = 0; 05716 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) 05717 shared_lastcall = ast_true(general_val); 05718 }
| 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 1385 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_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, parse_empty_options(), call_queue::periodicannouncefrequency, 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, call_queue::timeout, TIMEOUT_PRIORITY_APP, TIMEOUT_PRIORITY_CONF, call_queue::timeoutpriority, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_single_queue().
01386 { 01387 if (!strcasecmp(param, "musicclass") || 01388 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 01389 ast_string_field_set(q, moh, val); 01390 } else if (!strcasecmp(param, "announce")) { 01391 ast_string_field_set(q, announce, val); 01392 } else if (!strcasecmp(param, "context")) { 01393 ast_string_field_set(q, context, val); 01394 } else if (!strcasecmp(param, "timeout")) { 01395 q->timeout = atoi(val); 01396 if (q->timeout < 0) 01397 q->timeout = DEFAULT_TIMEOUT; 01398 } else if (!strcasecmp(param, "ringinuse")) { 01399 q->ringinuse = ast_true(val); 01400 } else if (!strcasecmp(param, "setinterfacevar")) { 01401 q->setinterfacevar = ast_true(val); 01402 } else if (!strcasecmp(param, "setqueuevar")) { 01403 q->setqueuevar = ast_true(val); 01404 } else if (!strcasecmp(param, "setqueueentryvar")) { 01405 q->setqueueentryvar = ast_true(val); 01406 } else if (!strcasecmp(param, "monitor-format")) { 01407 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 01408 } else if (!strcasecmp(param, "membermacro")) { 01409 ast_string_field_set(q, membermacro, val); 01410 } else if (!strcasecmp(param, "membergosub")) { 01411 ast_string_field_set(q, membergosub, val); 01412 } else if (!strcasecmp(param, "queue-youarenext")) { 01413 ast_string_field_set(q, sound_next, val); 01414 } else if (!strcasecmp(param, "queue-thereare")) { 01415 ast_string_field_set(q, sound_thereare, val); 01416 } else if (!strcasecmp(param, "queue-callswaiting")) { 01417 ast_string_field_set(q, sound_calls, val); 01418 } else if (!strcasecmp(param, "queue-quantity1")) { 01419 ast_string_field_set(q, queue_quantity1, val); 01420 } else if (!strcasecmp(param, "queue-quantity2")) { 01421 ast_string_field_set(q, queue_quantity2, val); 01422 } else if (!strcasecmp(param, "queue-holdtime")) { 01423 ast_string_field_set(q, sound_holdtime, val); 01424 } else if (!strcasecmp(param, "queue-minutes")) { 01425 ast_string_field_set(q, sound_minutes, val); 01426 } else if (!strcasecmp(param, "queue-minute")) { 01427 ast_string_field_set(q, sound_minute, val); 01428 } else if (!strcasecmp(param, "queue-seconds")) { 01429 ast_string_field_set(q, sound_seconds, val); 01430 } else if (!strcasecmp(param, "queue-thankyou")) { 01431 ast_string_field_set(q, sound_thanks, val); 01432 } else if (!strcasecmp(param, "queue-callerannounce")) { 01433 ast_string_field_set(q, sound_callerannounce, val); 01434 } else if (!strcasecmp(param, "queue-reporthold")) { 01435 ast_string_field_set(q, sound_reporthold, val); 01436 } else if (!strcasecmp(param, "announce-frequency")) { 01437 q->announcefrequency = atoi(val); 01438 } else if (!strcasecmp(param, "min-announce-frequency")) { 01439 q->minannouncefrequency = atoi(val); 01440 ast_debug(1, "%s=%s for queue '%s'\n", param, val, q->name); 01441 } else if (!strcasecmp(param, "announce-round-seconds")) { 01442 q->roundingseconds = atoi(val); 01443 /* Rounding to any other values just doesn't make sense... */ 01444 if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10 01445 || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) { 01446 if (linenum >= 0) { 01447 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01448 "using 0 instead for queue '%s' at line %d of queues.conf\n", 01449 val, param, q->name, linenum); 01450 } else { 01451 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01452 "using 0 instead for queue '%s'\n", val, param, q->name); 01453 } 01454 q->roundingseconds=0; 01455 } 01456 } else if (!strcasecmp(param, "announce-holdtime")) { 01457 if (!strcasecmp(val, "once")) 01458 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 01459 else if (ast_true(val)) 01460 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 01461 else 01462 q->announceholdtime = 0; 01463 } else if (!strcasecmp(param, "announce-position")) { 01464 if (!strcasecmp(val, "limit")) 01465 q->announceposition = ANNOUNCEPOSITION_LIMIT; 01466 else if (!strcasecmp(val, "more")) 01467 q->announceposition = ANNOUNCEPOSITION_MORE_THAN; 01468 else if (ast_true(val)) 01469 q->announceposition = ANNOUNCEPOSITION_YES; 01470 else 01471 q->announceposition = ANNOUNCEPOSITION_NO; 01472 } else if (!strcasecmp(param, "announce-position-limit")) { 01473 q->announcepositionlimit = atoi(val); 01474 } else if (!strcasecmp(param, "periodic-announce")) { 01475 if (strchr(val, ',')) { 01476 char *s, *buf = ast_strdupa(val); 01477 unsigned int i = 0; 01478 01479 while ((s = strsep(&buf, ",|"))) { 01480 if (!q->sound_periodicannounce[i]) 01481 q->sound_periodicannounce[i] = ast_str_create(16); 01482 ast_str_set(&q->sound_periodicannounce[i], 0, "%s", s); 01483 i++; 01484 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 01485 break; 01486 } 01487 q->numperiodicannounce = i; 01488 } else { 01489 ast_str_set(&q->sound_periodicannounce[0], 0, "%s", val); 01490 q->numperiodicannounce = 1; 01491 } 01492 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 01493 q->periodicannouncefrequency = atoi(val); 01494 } else if (!strcasecmp(param, "random-periodic-announce")) { 01495 q->randomperiodicannounce = ast_true(val); 01496 } else if (!strcasecmp(param, "retry")) { 01497 q->retry = atoi(val); 01498 if (q->retry <= 0) 01499 q->retry = DEFAULT_RETRY; 01500 } else if (!strcasecmp(param, "wrapuptime")) { 01501 q->wrapuptime = atoi(val); 01502 } else if (!strcasecmp(param, "autofill")) { 01503 q->autofill = ast_true(val); 01504 } else if (!strcasecmp(param, "monitor-type")) { 01505 if (!strcasecmp(val, "mixmonitor")) 01506 q->montype = 1; 01507 } else if (!strcasecmp(param, "autopause")) { 01508 q->autopause = ast_true(val); 01509 } else if (!strcasecmp(param, "maxlen")) { 01510 q->maxlen = atoi(val); 01511 if (q->maxlen < 0) 01512 q->maxlen = 0; 01513 } else if (!strcasecmp(param, "servicelevel")) { 01514 q->servicelevel= atoi(val); 01515 } else if (!strcasecmp(param, "strategy")) { 01516 int strategy; 01517 01518 /* We are a static queue and already have set this, no need to do it again */ 01519 if (failunknown) { 01520 return; 01521 } 01522 strategy = strat2int(val); 01523 if (strategy < 0) { 01524 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01525 val, q->name); 01526 q->strategy = QUEUE_STRATEGY_RINGALL; 01527 } 01528 if (strategy == q->strategy) { 01529 return; 01530 } 01531 if (strategy == QUEUE_STRATEGY_LINEAR) { 01532 ast_log(LOG_WARNING, "Changing to the linear strategy currently requires asterisk to be restarted.\n"); 01533 return; 01534 } 01535 q->strategy = strategy; 01536 } else if (!strcasecmp(param, "joinempty")) { 01537 parse_empty_options(val, &q->joinempty, 1); 01538 } else if (!strcasecmp(param, "leavewhenempty")) { 01539 parse_empty_options(val, &q->leavewhenempty, 0); 01540 } else if (!strcasecmp(param, "eventmemberstatus")) { 01541 q->maskmemberstatus = !ast_true(val); 01542 } else if (!strcasecmp(param, "eventwhencalled")) { 01543 if (!strcasecmp(val, "vars")) { 01544 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 01545 } else { 01546 q->eventwhencalled = ast_true(val) ? 1 : 0; 01547 } 01548 } else if (!strcasecmp(param, "reportholdtime")) { 01549 q->reportholdtime = ast_true(val); 01550 } else if (!strcasecmp(param, "memberdelay")) { 01551 q->memberdelay = atoi(val); 01552 } else if (!strcasecmp(param, "weight")) { 01553 q->weight = atoi(val); 01554 } else if (!strcasecmp(param, "timeoutrestart")) { 01555 q->timeoutrestart = ast_true(val); 01556 } else if (!strcasecmp(param, "defaultrule")) { 01557 ast_string_field_set(q, defaultrule, val); 01558 } else if (!strcasecmp(param, "timeoutpriority")) { 01559 if (!strcasecmp(val, "conf")) { 01560 q->timeoutpriority = TIMEOUT_PRIORITY_CONF; 01561 } else { 01562 q->timeoutpriority = TIMEOUT_PRIORITY_APP; 01563 } 01564 } else if (failunknown) { 01565 if (linenum >= 0) { 01566 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 01567 q->name, param, linenum); 01568 } else { 01569 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 01570 } 01571 } 01572 }
| static char* queue_show | ( | struct ast_cli_entry * | e, | |
| int | cmd, | |||
| struct ast_cli_args * | a | |||
| ) | [static] |
Definition at line 6240 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.
06241 { 06242 switch ( cmd ) { 06243 case CLI_INIT: 06244 e->command = "queue show"; 06245 e->usage = 06246 "Usage: queue show\n" 06247 " Provides summary information on a specified queue.\n"; 06248 return NULL; 06249 case CLI_GENERATE: 06250 return complete_queue_show(a->line, a->word, a->pos, a->n); 06251 } 06252 06253 return __queues_show(NULL, a->fd, a->argc, a->argv); 06254 }
| static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 3426 of file app_queue.c.
References ast_free.
03427 { 03428 struct queue_transfer_ds *qtds = data; 03429 ast_free(qtds); 03430 }
| 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 3449 of file app_queue.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::opos, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, ast_channel::uniqueid, and update_queue().
03450 { 03451 struct queue_transfer_ds *qtds = data; 03452 struct queue_ent *qe = qtds->qe; 03453 struct member *member = qtds->member; 03454 time_t callstart = qtds->starttime; 03455 int callcompletedinsl = qtds->callcompletedinsl; 03456 struct ast_datastore *datastore; 03457 03458 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 03459 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 03460 (long) (time(NULL) - callstart), qe->opos); 03461 03462 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 03463 03464 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 03465 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 03466 ast_channel_datastore_remove(old_chan, datastore); 03467 } else { 03468 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 03469 } 03470 }
| static struct call_queue* queue_unref | ( | struct call_queue * | q | ) | [static, read] |
Definition at line 894 of file app_queue.c.
References ao2_ref.
Referenced by queue_exec().
00895 { 00896 ao2_ref(q, -1); 00897 return q; 00898 }
| static void recalc_holdtime | ( | struct queue_ent * | qe, | |
| int | newholdtime | |||
| ) | [static] |
Definition at line 2224 of file app_queue.c.
References ao2_lock(), ao2_unlock(), call_queue::holdtime, and queue_ent::parent.
Referenced by try_calling().
02225 { 02226 int oldvalue; 02227 02228 /* Calculate holdtime using an exponential average */ 02229 /* Thanks to SRT for this contribution */ 02230 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 02231 02232 ao2_lock(qe->parent); 02233 oldvalue = qe->parent->holdtime; 02234 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 02235 ao2_unlock(qe->parent); 02236 }
| static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Record that a caller gave up on waiting in queue.
Definition at line 2784 of file app_queue.c.
References ao2_lock(), ao2_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, manager_event, call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, set_queue_variables(), queue_ent::start, and ast_channel::uniqueid.
Referenced by queue_exec(), and try_calling().
02785 { 02786 ao2_lock(qe->parent); 02787 set_queue_variables(qe->parent, qe->chan); 02788 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 02789 "Queue: %s\r\n" 02790 "Uniqueid: %s\r\n" 02791 "Position: %d\r\n" 02792 "OriginalPosition: %d\r\n" 02793 "HoldTime: %d\r\n", 02794 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 02795 02796 qe->parent->callsabandoned++; 02797 ao2_unlock(qe->parent); 02798 }
| static int reload | ( | void | ) | [static] |
Definition at line 7280 of file app_queue.c.
References AST_FLAGS_ALL, ast_unload_realtime(), and reload_handler().
07281 { 07282 struct ast_flags mask = {AST_FLAGS_ALL,}; 07283 ast_unload_realtime("queue_members"); 07284 reload_handler(1, &mask, NULL); 07285 return 0; 07286 }
| static int reload_handler | ( | int | reload, | |
| struct ast_flags * | mask, | |||
| const char * | queuename | |||
| ) | [static] |
The command center for all reload operations.
Whenever any piece of queue information is to be reloaded, this function is called. It interprets the flags set in the mask parameter and acts based on how they are set.
| reload | True if we are reloading information, false if we are loading information for the first time. | |
| mask | A bitmask which tells the handler what actions to take | |
| queuename | The name of the queue on which we wish to take action |
| 0 | All reloads were successful | |
| non-zero | There was a failure |
Definition at line 6052 of file app_queue.c.
References ast_test_flag, clear_stats(), QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, QUEUE_RELOAD_RULES, QUEUE_RESET_STATS, reload_queue_rules(), and reload_queues().
Referenced by handle_queue_reload(), handle_queue_reset(), load_module(), manager_queue_reload(), manager_queue_reset(), and reload().
06053 { 06054 int res = 0; 06055 06056 if (ast_test_flag(mask, QUEUE_RELOAD_RULES)) { 06057 res |= reload_queue_rules(reload); 06058 } 06059 if (ast_test_flag(mask, QUEUE_RESET_STATS)) { 06060 res |= clear_stats(queuename); 06061 } 06062 if (ast_test_flag(mask, (QUEUE_RELOAD_PARAMETERS | QUEUE_RELOAD_MEMBER))) { 06063 res |= reload_queues(reload, mask, queuename); 06064 } 06065 return res; 06066 }
| static void reload_queue_members | ( | void | ) | [static] |
Reload dynamic queue members persisted into the astdb.
Definition at line 4667 of file app_queue.c.
References add_to_queue(), ao2_lock(), ao2_t_find, ao2_unlock(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), ast_debug, ast_log(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, load_realtime_queue(), LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, OBJ_POINTER, member::paused, member::penalty, PM_MAX_LEN, queue_t_unref, queues, RES_OUTOFMEMORY, and member::state_interface.
Referenced by load_module().
04668 { 04669 char *cur_ptr; 04670 const char *queue_name; 04671 char *member; 04672 char *interface; 04673 char *membername = NULL; 04674 char *state_interface; 04675 char *penalty_tok; 04676 int penalty = 0; 04677 char *paused_tok; 04678 int paused = 0; 04679 struct ast_db_entry *db_tree; 04680 struct ast_db_entry *entry; 04681 struct call_queue *cur_queue; 04682 char queue_data[PM_MAX_LEN]; 04683 04684 ao2_lock(queues); 04685 04686 /* Each key in 'pm_family' is the name of a queue */ 04687 db_tree = ast_db_gettree(pm_family, NULL); 04688 for (entry = db_tree; entry; entry = entry->next) { 04689 04690 queue_name = entry->key + strlen(pm_family) + 2; 04691 04692 { 04693 struct call_queue tmpq = { 04694 .name = queue_name, 04695 }; 04696 cur_queue = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Reload queue members"); 04697 } 04698 04699 if (!cur_queue) 04700 cur_queue = load_realtime_queue(queue_name); 04701 04702 if (!cur_queue) { 04703 /* If the queue no longer exists, remove it from the 04704 * database */ 04705 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 04706 ast_db_del(pm_family, queue_name); 04707 continue; 04708 } 04709 04710 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) { 04711 queue_t_unref(cur_queue, "Expire reload reference"); 04712 continue; 04713 } 04714 04715 cur_ptr = queue_data; 04716 while ((member = strsep(&cur_ptr, ",|"))) { 04717 if (ast_strlen_zero(member)) 04718 continue; 04719 04720 interface = strsep(&member, ";"); 04721 penalty_tok = strsep(&member, ";"); 04722 paused_tok = strsep(&member, ";"); 04723 membername = strsep(&member, ";"); 04724 state_interface = strsep(&member, ";"); 04725 04726 if (!penalty_tok) { 04727 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 04728 break; 04729 } 04730 penalty = strtol(penalty_tok, NULL, 10); 04731 if (errno == ERANGE) { 04732 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 04733 break; 04734 } 04735 04736 if (!paused_tok) { 04737 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 04738 break; 04739 } 04740 paused = strtol(paused_tok, NULL, 10); 04741 if ((errno == ERANGE) || paused < 0 || paused > 1) { 04742 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 04743 break; 04744 } 04745 04746 ast_debug(1, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 04747 04748 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) { 04749 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 04750 break; 04751 } 04752 } 04753 queue_t_unref(cur_queue, "Expire reload reference"); 04754 } 04755 04756 ao2_unlock(queues); 04757 if (db_tree) { 04758 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 04759 ast_db_freetree(db_tree); 04760 } 04761 }
| static int reload_queue_rules | ( | int | reload | ) | [static] |
Reload the rules defined in queuerules.conf.
| reload | If 1, then only process queuerules.conf if the file has changed since the last time we inspected it. |
Definition at line 5650 of file app_queue.c.
References ast_calloc, ast_category_browse(), ast_config_destroy(), ast_config_load, ast_copy_string(), ast_free, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log(), AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_SUCCESS, ast_variable_browse(), CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, insert_penaltychange(), ast_variable::lineno, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_variable::name, rule_list::name, ast_variable::next, rule_list::rules, and ast_variable::value.
Referenced by reload_handler().
05651 { 05652 struct ast_config *cfg; 05653 struct rule_list *rl_iter, *new_rl; 05654 struct penalty_rule *pr_iter; 05655 char *rulecat = NULL; 05656 struct ast_variable *rulevar = NULL; 05657 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 05658 05659 if (!(cfg = ast_config_load("queuerules.conf", config_flags))) { 05660 ast_log(LOG_NOTICE, "No queuerules.conf file found, queues will not follow penalty rules\n"); 05661 return AST_MODULE_LOAD_SUCCESS; 05662 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 05663 ast_log(LOG_NOTICE, "queuerules.conf has not changed since it was last loaded. Not taking any action.\n"); 05664 return AST_MODULE_LOAD_SUCCESS; 05665 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 05666 ast_log(LOG_ERROR, "Config file queuerules.conf is in an invalid format. Aborting.\n"); 05667 return AST_MODULE_LOAD_SUCCESS; 05668 } 05669 05670 AST_LIST_LOCK(&rule_lists); 05671 while ((rl_iter = AST_LIST_REMOVE_HEAD(&rule_lists, list))) { 05672 while ((pr_iter = AST_LIST_REMOVE_HEAD(&rl_iter->rules, list))) 05673 ast_free(pr_iter); 05674 ast_free(rl_iter); 05675 } 05676 while ((rulecat = ast_category_browse(cfg, rulecat))) { 05677 if (!(new_rl = ast_calloc(1, sizeof(*new_rl)))) { 05678 AST_LIST_UNLOCK(&rule_lists); 05679 return AST_MODULE_LOAD_FAILURE; 05680 } else { 05681 ast_copy_string(new_rl->name, rulecat, sizeof(new_rl->name)); 05682 AST_LIST_INSERT_TAIL(&rule_lists, new_rl, list); 05683 for (rulevar = ast_variable_browse(cfg, rulecat); rulevar; rulevar = rulevar->next) 05684 if(!strcasecmp(rulevar->name, "penaltychange")) 05685 insert_penaltychange(new_rl->name, rulevar->value, rulevar->lineno); 05686 else 05687 ast_log(LOG_WARNING, "Don't know how to handle rule type '%s' on line %d\n", rulevar->name, rulevar->lineno); 05688 } 05689 } 05690 AST_LIST_UNLOCK(&rule_lists); 05691 05692 ast_config_destroy(cfg); 05693 05694 return AST_MODULE_LOAD_SUCCESS; 05695 }
| static int reload_queues | ( | int | reload, | |
| struct ast_flags * | mask, | |||
| const char * | queuename | |||
| ) | [static] |
reload the queues.conf file
This function reloads the information in the general section of the queues.conf file and potentially more, depending on the value of mask.
| reload | 0 if we are calling this the first time, 1 every other time | |
| mask | Gives flags telling us what information to actually reload | |
| queuename | If set to a non-zero string, then only reload information from that particular queue. Otherwise inspect all queues |
| -1 | Failure occurred | |
| 0 | All clear! |
Definition at line 5964 of file app_queue.c.
References ao2_callback, ao2_lock(), ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_config_load, ast_log(), ast_strlen_zero(), ast_test_flag, CONFIG_FLAG_FILEUNCHANGED, CONFIG_STATUS_FILEINVALID, CONFIG_STATUS_FILEUNCHANGED, kill_dead_queues(), LOG_ERROR, LOG_NOTICE, mark_dead_and_unfound(), OBJ_MULTIPLE, OBJ_NODATA, OBJ_UNLINK, QUEUE_RELOAD_PARAMETERS, queue_set_global_params(), queues, and reload_single_queue().
Referenced by reload_handler().
05965 { 05966 struct ast_config *cfg; 05967 char *cat; 05968 struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; 05969 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 05970 05971 if (!(cfg = ast_config_load("queues.conf", config_flags))) { 05972 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 05973 return -1; 05974 } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { 05975 return 0; 05976 } else if (cfg == CONFIG_STATUS_FILEINVALID) { 05977 ast_log(LOG_ERROR, "Config file queues.conf is in an invalid format. Aborting.\n"); 05978 return -1; 05979 } 05980 05981 /* We've made it here, so it looks like we're doing operations on all queues. */ 05982 ao2_lock(queues); 05983 05984 /* Mark all queues as dead for the moment if we're reloading queues. 05985 * For clarity, we could just be reloading members, in which case we don't want to mess 05986 * with the other queue parameters at all*/ 05987 if (queue_reload) { 05988 ao2_callback(queues, OBJ_NODATA, mark_dead_and_unfound, (char *) queuename); 05989 } 05990 05991 /* Chug through config file */ 05992 cat = NULL; 05993 while ((cat = ast_category_browse(cfg, cat)) ) { 05994 if (!strcasecmp(cat, "general") && queue_reload) { 05995 queue_set_global_params(cfg); 05996 continue; 05997 } 05998 if (ast_strlen_zero(queuename) || !strcasecmp(cat, queuename)) 05999 reload_single_queue(cfg, mask, cat); 06000 } 06001 06002 ast_config_destroy(cfg); 06003 /* Unref all the dead queues if we were reloading queues */ 06004 if (queue_reload) { 06005 ao2_callback(queues, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_queues, (char *) queuename); 06006 } 06007 ao2_unlock(queues); 06008 return 0; 06009 }
| static void reload_single_member | ( | const char * | memberdata, | |
| struct call_queue * | q | |||
| ) | [static] |
reload information pertaining to a single member
This function is called when a member = line is encountered in queues.conf.
| memberdata | The part after member = in the config file | |
| q | The queue to which this member belongs |
Definition at line 5728 of file app_queue.c.
References ao2_find, ao2_link, ao2_ref, AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strip(), ast_strlen_zero(), create_queue_member(), member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, OBJ_POINTER, OBJ_UNLINK, parse(), member::paused, and member::penalty.
Referenced by reload_single_queue().
05729 { 05730 char *membername, *interface, *state_interface, *tmp; 05731 char *parse; 05732 struct member *cur, *newm; 05733 struct member tmpmem; 05734 int penalty; 05735 AST_DECLARE_APP_ARGS(args, 05736 AST_APP_ARG(interface); 05737 AST_APP_ARG(penalty); 05738 AST_APP_ARG(membername); 05739 AST_APP_ARG(state_interface); 05740 ); 05741 05742 if (ast_strlen_zero(memberdata)) { 05743 ast_log(LOG_WARNING, "Empty queue member definition. Moving on!\n"); 05744 return; 05745 } 05746 05747 /* Add a new member */ 05748 parse = ast_strdupa(memberdata); 05749 05750 AST_STANDARD_APP_ARGS(args, parse); 05751 05752 interface = args.interface; 05753 if (!ast_strlen_zero(args.penalty)) { 05754 tmp = args.penalty; 05755 ast_strip(tmp); 05756 penalty = atoi(tmp); 05757 if (penalty < 0) { 05758 penalty = 0; 05759 } 05760 } else { 05761 penalty = 0; 05762 } 05763 05764 if (!ast_strlen_zero(args.membername)) { 05765 membername = args.membername; 05766 ast_strip(membername); 05767 } else { 05768 membername = interface; 05769 } 05770 05771 if (!ast_strlen_zero(args.state_interface)) { 05772 state_interface = args.state_interface; 05773 ast_strip(state_interface); 05774 } else { 05775 state_interface = interface; 05776 } 05777 05778 /* Find the old position in the list */ 05779 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 05780 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 05781 if ((newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface))) { 05782 ao2_link(q->members, newm); 05783 ao2_ref(newm, -1); 05784 } 05785 newm = NULL; 05786 05787 if (cur) { 05788 ao2_ref(cur, -1); 05789 } else { 05790 q->membercount++; 05791 } 05792 }
| static void reload_single_queue | ( | struct ast_config * | cfg, | |
| struct ast_flags * | mask, | |||
| const char * | queuename | |||
| ) | [static] |
Reload information pertaining to a particular queue.
Once we have isolated a queue within reload_queues, we call this. This will either reload information for the queue or if we're just reloading member information, we'll just reload that without touching other settings within the queue
| cfg | The configuration which we are reading | |
| mask | Tells us what information we need to reload | |
| queuename | The name of the queue we are reloading information from |
| void |
Definition at line 5834 of file app_queue.c.
References alloc_queue(), ao2_callback, ao2_lock(), ao2_t_find, ao2_unlock(), ast_atomic_fetchadd_int(), ast_log(), ast_test_flag, ast_variable_browse(), ast_variable_retrieve(), call_queue::found, init_queue(), kill_dead_members(), ast_variable::lineno, LOG_WARNING, mark_member_dead(), call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, OBJ_MULTIPLE, OBJ_NODATA, OBJ_POINTER, OBJ_UNLINK, QUEUE_RELOAD_MEMBER, QUEUE_RELOAD_PARAMETERS, queue_set_param(), QUEUE_STRATEGY_RINGALL, queue_t_unref, queues, queues_t_link, reload_single_member(), strat2int(), call_queue::strategy, ast_variable::value, var, and call_queue::weight.
Referenced by reload_queues().
05835 { 05836 int new; 05837 struct call_queue *q = NULL; 05838 /*We're defining a queue*/ 05839 struct call_queue tmpq = { 05840 .name = queuename, 05841 }; 05842 const char *tmpvar; 05843 const int queue_reload = ast_test_flag(mask, QUEUE_RELOAD_PARAMETERS); 05844 const int member_reload = ast_test_flag(mask, QUEUE_RELOAD_MEMBER); 05845 int prev_weight = 0; 05846 struct ast_variable *var; 05847 if (!(q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Find queue for reload"))) { 05848 if (queue_reload) { 05849 /* Make one then */ 05850 if (!(q = alloc_queue(queuename))) { 05851 return; 05852 } 05853 } else { 05854 /* Since we're not reloading queues, this means that we found a queue 05855 * in the configuration file which we don't know about yet. Just return. 05856 */ 05857 return; 05858 } 05859 new = 1; 05860 } else { 05861 new = 0; 05862 } 05863 05864 if (!new) { 05865 ao2_lock(q); 05866 prev_weight = q->weight ? 1 : 0; 05867 } 05868 /* Check if we already found a queue with this name in the config file */ 05869 if (q->found) { 05870 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", queuename); 05871 if (!new) { 05872 /* It should be impossible to *not* hit this case*/ 05873 ao2_unlock(q); 05874 } 05875 queue_t_unref(q, "We exist! Expiring temporary pointer"); 05876 return; 05877 } 05878 /* Due to the fact that the "linear" strategy will have a different allocation 05879 * scheme for queue members, we must devise the queue's strategy before other initializations. 05880 * To be specific, the linear strategy needs to function like a linked list, meaning the ao2 05881 * container used will have only a single bucket instead of the typical number. 05882 */ 05883 if (queue_reload) { 05884 if ((tmpvar = ast_variable_retrieve(cfg, queuename, "strategy"))) { 05885 q->strategy = strat2int(tmpvar); 05886 if (q->strategy < 0) { 05887 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 05888 tmpvar, q->name); 05889 q->strategy = QUEUE_STRATEGY_RINGALL; 05890 } 05891 } else { 05892 q->strategy = QUEUE_STRATEGY_RINGALL; 05893 } 05894 init_queue(q); 05895 } 05896 if (member_reload) { 05897 q->membercount = 0; 05898 ao2_callback(q->members, OBJ_NODATA, mark_member_dead, NULL); 05899 } 05900 for (var = ast_variable_browse(cfg, queuename); var; var = var->next) { 05901 if (member_reload && !strcasecmp(var->name, "member")) { 05902 reload_single_member(var->value, q); 05903 } else if (queue_reload) { 05904 queue_set_param(q, var->name, var->value, var->lineno, 1); 05905 } 05906 } 05907 /* At this point, we've determined if the queue has a weight, so update use_weight 05908 * as appropriate 05909 */ 05910 if (!q->weight && prev_weight) { 05911 ast_atomic_fetchadd_int(&use_weight, -1); 05912 } 05913 else if (q->weight && !prev_weight) { 05914 ast_atomic_fetchadd_int(&use_weight, +1); 05915 } 05916 05917 /* Free remaining members marked as delme */ 05918 if (member_reload) { 05919 ao2_callback(q->members, OBJ_NODATA | OBJ_MULTIPLE | OBJ_UNLINK, kill_dead_members, q); 05920 } 05921 05922 if (new) { 05923 queues_t_link(queues, q, "Add queue to container"); 05924 } else { 05925 ao2_unlock(q); 05926 } 05927 queue_t_unref(q, "Expiring creation reference"); 05928 }
| 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 4403 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, RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.
Referenced by handle_queue_remove_member(), manager_remove_queue_member(), and rqm_exec().
04404 { 04405 struct call_queue *q, tmpq = { 04406 .name = queuename, 04407 }; 04408 struct member *mem, tmpmem; 04409 int res = RES_NOSUCHQUEUE; 04410 04411 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04412 if ((q = ao2_t_find(queues, &tmpq, OBJ_POINTER, "Temporary reference for interface removal"))) { 04413 ao2_lock(queues); 04414 ao2_lock(q); 04415 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 04416 /* XXX future changes should beware of this assumption!! */ 04417 if (!mem->dynamic) { 04418 ao2_ref(mem, -1); 04419 ao2_unlock(q); 04420 queue_t_unref(q, "Interface wasn't dynamic, expiring temporary reference"); 04421 ao2_unlock(queues); 04422 return RES_NOT_DYNAMIC; 04423 } 04424 q->membercount--; 04425 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 04426 "Queue: %s\r\n" 04427 "Location: %s\r\n" 04428 "MemberName: %s\r\n", 04429 q->name, mem->interface, mem->membername); 04430 ao2_unlink(q->members, mem); 04431 ao2_ref(mem, -1); 04432 04433 if (queue_persistent_members) 04434 dump_queue_members(q); 04435 04436 res = RES_OKAY; 04437 } else { 04438 res = RES_EXISTS; 04439 } 04440 ao2_unlock(q); 04441 ao2_unlock(queues); 04442 queue_t_unref(q, "Expiring temporary reference"); 04443 } 04444 04445 return res; 04446 }
| 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 2467 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_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_request(), ast_set_flag, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verb, queue_ent::cancel_answered_elsewhere, 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, ast_channel::name, call_queue::name, ast_channel::nativeformats, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, status, callattempt::stillgoing, ast_channel::uniqueid, update_status(), ast_cdr::userfield, vars2manager(), ast_channel::whentohangup, and call_queue::wrapuptime.
Referenced by ring_one().
02468 { 02469 int res; 02470 int status; 02471 char tech[256]; 02472 char *location; 02473 const char *macrocontext, *macroexten; 02474 02475 /* on entry here, we know that tmp->chan == NULL */ 02476 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) || 02477 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) { 02478 ast_debug(1, "Wrapuptime not yet expired on queue %s for %s\n", 02479 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface); 02480 if (qe->chan->cdr) 02481 ast_cdr_busy(qe->chan->cdr); 02482 tmp->stillgoing = 0; 02483 (*busies)++; 02484 return 0; 02485 } 02486 02487 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 02488 ast_debug(1, "%s in use, can't receive call\n", tmp->interface); 02489 if (qe->chan->cdr) 02490 ast_cdr_busy(qe->chan->cdr); 02491 tmp->stillgoing = 0; 02492 return 0; 02493 } 02494 02495 if (tmp->member->paused) { 02496 ast_debug(1, "%s paused, can't receive call\n", tmp->interface); 02497 if (qe->chan->cdr) 02498 ast_cdr_busy(qe->chan->cdr); 02499 tmp->stillgoing = 0; 02500 return 0; 02501 } 02502 if (use_weight && compare_weight(qe->parent,tmp->member)) { 02503 ast_debug(1, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 02504 if (qe->chan->cdr) 02505 ast_cdr_busy(qe->chan->cdr); 02506 tmp->stillgoing = 0; 02507 (*busies)++; 02508 return 0; 02509 } 02510 02511 ast_copy_string(tech, tmp->interface, sizeof(tech)); 02512 if ((location = strchr(tech, '/'))) 02513 *location++ = '\0'; 02514 else 02515 location = ""; 02516 02517 /* Request the peer */ 02518 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); 02519 if (!tmp->chan) { /* If we can't, just go on to the next call */ 02520 if (qe->chan->cdr) 02521 ast_cdr_busy(qe->chan->cdr); 02522 tmp->stillgoing = 0; 02523 02524 ao2_lock(qe->parent); 02525 update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); 02526 qe->parent->rrpos++; 02527 qe->linpos++; 02528 ao2_unlock(qe->parent); 02529 02530 (*busies)++; 02531 return 0; 02532 } 02533 02534 if (qe->cancel_answered_elsewhere) { 02535 ast_set_flag(tmp->chan, AST_FLAG_ANSWERED_ELSEWHERE); 02536 } 02537 tmp->chan->appl = "AppQueue"; 02538 tmp->chan->data = "(Outgoing Line)"; 02539 memset(&tmp->chan->whentohangup, 0, sizeof(tmp->chan->whentohangup)); 02540 if (tmp->chan->cid.cid_num) 02541 ast_free(tmp->chan->cid.cid_num); 02542 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); 02543 if (tmp->chan->cid.cid_name) 02544 ast_free(tmp->chan->cid.cid_name); 02545 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); 02546 if (tmp->chan->cid.cid_ani) 02547 ast_free(tmp->chan->cid.cid_ani); 02548 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); 02549 02550 /* Inherit specially named variables from parent channel */ 02551 ast_channel_inherit_variables(qe->chan, tmp->chan); 02552 ast_channel_datastore_inherit(qe->chan, tmp->chan); 02553 02554 /* Presense of ADSI CPE on outgoing channel follows ours */ 02555 tmp->chan->adsicpe = qe->chan->adsicpe; 02556 02557 /* Inherit context and extension */ 02558 ast_channel_lock(qe->chan); 02559 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 02560 ast_string_field_set(tmp->chan, dialcontext, ast_strlen_zero(macrocontext) ? qe->chan->context : macrocontext); 02561 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 02562 if (!ast_strlen_zero(macroexten)) 02563 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 02564 else 02565 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 02566 if (ast_cdr_isset_unanswered()) { 02567 /* they want to see the unanswered dial attempts! */ 02568 /* set up the CDR fields on all the CDRs to give sensical information */ 02569 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name); 02570 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid); 02571 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel); 02572 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src); 02573 strcpy(tmp->chan->cdr->dst, qe->chan->exten); 02574 strcpy(tmp->chan->cdr->dcontext, qe->chan->context); 02575 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp); 02576 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata); 02577 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags; 02578 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode); 02579 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield); 02580 } 02581 ast_channel_unlock(qe->chan); 02582 02583 /* Place the call, but don't wait on the answer */ 02584 if ((res = ast_call(tmp->chan, location, 0))) { 02585 /* Again, keep going even if there's an error */ 02586 ast_debug(1, "ast call on peer returned %d\n", res); 02587 ast_verb(3, "Couldn't call %s\n", tmp->interface); 02588 do_hang(tmp); 02589 (*busies)++; 02590 update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); 02591 return 0; 02592 } else if (qe->parent->eventwhencalled) { 02593 char vars[2048]; 02594 02595 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 02596 "Queue: %s\r\n" 02597 "AgentCalled: %s\r\n" 02598 "AgentName: %s\r\n" 02599 "ChannelCalling: %s\r\n" 02600 "DestinationChannel: %s\r\n" 02601 "CallerIDNum: %s\r\n" 02602 "CallerIDName: %s\r\n" 02603 "Context: %s\r\n" 02604 "Extension: %s\r\n" 02605 "Priority: %d\r\n" 02606 "Uniqueid: %s\r\n" 02607 "%s", 02608 qe->parent->name, tmp->interface, tmp->member->membername, qe->chan->name, tmp->chan->name, 02609 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", 02610 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", 02611 qe->chan->context, qe->chan->exten, qe->chan->priority, qe->chan->uniqueid, 02612 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 02613 ast_verb(3, "Called %s\n", tmp->interface); 02614 } 02615 02616 update_status(qe->parent, tmp->member, ast_device_state(tmp->member->state_interface)); 02617 return 1; 02618 }
| 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 2646 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().
02647 { 02648 int ret = 0; 02649 02650 while (ret == 0) { 02651 struct callattempt *best = find_best(outgoing); 02652 if (!best) { 02653 ast_debug(1, "Nobody left to try ringing in queue\n"); 02654 break; 02655 } 02656 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 02657 struct callattempt *cur; 02658 /* Ring everyone who shares this best metric (for ringall) */ 02659 for (cur = outgoing; cur; cur = cur->q_next) { 02660 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 02661 ast_debug(1, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 02662 ret |= ring_entry(qe, cur, busies); 02663 } 02664 } 02665 } else { 02666 /* Ring just the best channel */ 02667 ast_debug(1, "Trying '%s' with metric %d\n", best->interface, best->metric); 02668 ret = ring_entry(qe, best, busies); 02669 } 02670 02671 /* If we have timed out, break out */ 02672 if (qe->expire && (time(NULL) >= qe->expire)) { 02673 ast_debug(1, "Queue timed out while ringing members.\n"); 02674 ret = 0; 02675 break; 02676 } 02677 } 02678 02679 return ret; 02680 }
| 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 2801 of file app_queue.c.
References ast_queue_log(), ast_verb, call_queue::autopause, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, manager_event, ast_channel::name, call_queue::name, queue_ent::parent, QUEUE_EVENT_VARIABLES, set_member_paused(), ast_channel::uniqueid, and vars2manager().
Referenced by wait_for_answer().
02802 { 02803 ast_verb(3, "Nobody picked up in %d ms\n", rnatime); 02804 if (qe->parent->eventwhencalled) { 02805 char vars[2048]; 02806 02807 manager_event(EVENT_FLAG_AGENT, "AgentRingNoAnswer", 02808 "Queue: %s\r\n" 02809 "Uniqueid: %s\r\n" 02810 "Channel: %s\r\n" 02811 "Member: %s\r\n" 02812 "MemberName: %s\r\n" 02813 "Ringtime: %d\r\n" 02814 "%s", 02815 qe->parent->name, 02816 qe->chan->uniqueid, 02817 qe->chan->name, 02818 interface, 02819 membername, 02820 rnatime, 02821 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 02822 } 02823 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 02824 if (qe->parent->autopause && pause) { 02825 if (!set_member_paused(qe->parent->name, interface, "Auto-Pause", 1)) { 02826 ast_verb(3, "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); 02827 } else { 02828 ast_verb(3, "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 02829 } 02830 } 02831 return; 02832 }
| static int rqm_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
RemoveQueueMember application.
Definition at line 4836 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_strlen_zero(), LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and ast_channel::uniqueid.
Referenced by load_module().
04837 { 04838 int res=-1; 04839 char *parse, *temppos = NULL; 04840 AST_DECLARE_APP_ARGS(args, 04841 AST_APP_ARG(queuename); 04842 AST_APP_ARG(interface); 04843 AST_APP_ARG(options); 04844 ); 04845 04846 04847 if (ast_strlen_zero(data)) { 04848 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[,interface[,options]])\n"); 04849 return -1; 04850 } 04851 04852 parse = ast_strdupa(data); 04853 04854 AST_STANDARD_APP_ARGS(args, parse); 04855 04856 if (ast_strlen_zero(args.interface)) { 04857 args.interface = ast_strdupa(chan->name); 04858 temppos = strrchr(args.interface, '-'); 04859 if (temppos) 04860 *temppos = '\0'; 04861 } 04862 04863 ast_debug(1, "queue: %s, member: %s\n", args.queuename, args.interface); 04864 04865 switch (remove_from_queue(args.queuename, args.interface)) { 04866 case RES_OKAY: 04867 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 04868 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 04869 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 04870 res = 0; 04871 break; 04872 case RES_EXISTS: 04873 ast_debug(1, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 04874 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 04875 res = 0; 04876 break; 04877 case RES_NOSUCHQUEUE: 04878 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 04879 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 04880 res = 0; 04881 break; 04882 case RES_NOT_DYNAMIC: 04883 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 04884 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 04885 res = 0; 04886 break; 04887 } 04888 04889 return res; 04890 }
| static void rt_handle_member_record | ( | struct call_queue * | q, | |
| char * | interface, | |||
| const char * | rt_uniqueid, | |||
| const char * | membername, | |||
| const char * | penalty_str, | |||
| const char * | paused_str, | |||
| const char * | state_interface | |||
| ) | [static] |
Find rt member record to update otherwise create one.
Search for member in queue, if found update penalty/paused state, if no member exists create one flag it as a RT member and add to queue member list.
Definition at line 1580 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_link, ao2_ref, ast_copy_string(), ast_log(), ast_queue_log(), ast_strlen_zero(), create_queue_member(), member::dead, member::interface, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::rt_uniqueid, S_OR, and member::state_interface.
Referenced by find_queue_by_name_rt(), and update_realtime_members().
01581 { 01582 struct member *m; 01583 struct ao2_iterator mem_iter; 01584 int penalty = 0; 01585 int paused = 0; 01586 int found = 0; 01587 01588 if (ast_strlen_zero(rt_uniqueid)) { 01589 ast_log(LOG_WARNING, "Realtime field uniqueid is empty for member %s\n", S_OR(membername, "NULL")); 01590 return; 01591 } 01592 01593 if (penalty_str) { 01594 penalty = atoi(penalty_str); 01595 if (penalty < 0) 01596 penalty = 0; 01597 } 01598 01599 if (paused_str) { 01600 paused = atoi(paused_str); 01601 if (paused < 0) 01602 paused = 0; 01603 } 01604 01605 /* Find member by realtime uniqueid and update */ 01606 mem_iter = ao2_iterator_init(q->members, 0); 01607 while ((m = ao2_iterator_next(&mem_iter))) { 01608 if (!strcasecmp(m->rt_uniqueid, rt_uniqueid)) { 01609 m->dead = 0; /* Do not delete this one. */ 01610 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 01611 if (paused_str) 01612 m->paused = paused; 01613 if (strcasecmp(state_interface, m->state_interface)) { 01614 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); 01615 } 01616 m->penalty = penalty; 01617 found = 1; 01618 ao2_ref(m, -1); 01619 break; 01620 } 01621 ao2_ref(m, -1); 01622 } 01623 ao2_iterator_destroy(&mem_iter); 01624 01625 /* Create a new member */ 01626 if (!found) { 01627 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { 01628 m->dead = 0; 01629 m->realtime = 1; 01630 ast_copy_string(m->rt_uniqueid, rt_uniqueid, sizeof(m->rt_uniqueid)); 01631 ast_queue_log(q->name, "REALTIME", m->interface, "ADDMEMBER", "%s", ""); 01632 ao2_link(q->members, m); 01633 ao2_ref(m, -1); 01634 m = NULL; 01635 q->membercount++; 01636 } 01637 } 01638 }
| static int say_periodic_announcement | ( | struct queue_ent * | qe, | |
| int | ringing | |||
| ) | [static] |
Playback announcement to queued members if peroid has elapsed.
Definition at line 2731 of file app_queue.c.
References AST_CONTROL_RINGING, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_random(), ast_str_buffer(), ast_str_strlen(), ast_verb, queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::moh, call_queue::numperiodicannounce, queue_ent::parent, call_queue::periodicannouncefrequency, play_file(), call_queue::randomperiodicannounce, call_queue::sound_periodicannounce, and valid_exit().
Referenced by queue_exec(), and wait_our_turn().
02732 { 02733 int res = 0; 02734 time_t now; 02735 02736 /* Get the current time */ 02737 time(&now); 02738 02739 /* Check to see if it is time to announce */ 02740 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 02741 return 0; 02742 02743 /* Stop the music on hold so we can play our own file */ 02744 if (ringing) 02745 ast_indicate(qe->chan,-1); 02746 else 02747 ast_moh_stop(qe->chan); 02748 02749 ast_verb(3, "Playing periodic announcement\n"); 02750 02751 if (qe->parent->randomperiodicannounce && qe->parent->numperiodicannounce) { 02752 qe->last_periodic_announce_sound = ((unsigned long) ast_random()) % qe->parent->numperiodicannounce; 02753 } else if (qe->last_periodic_announce_sound >= qe->parent->numperiodicannounce || 02754 ast_str_strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]) == 0) { 02755 qe->last_periodic_announce_sound = 0; 02756 } 02757 02758 /* play the announcement */ 02759 res = play_file(qe->chan, ast_str_buffer(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])); 02760 02761 if (res > 0 && !valid_exit(qe, res)) 02762 res = 0; 02763 02764 /* Resume Music on Hold if the caller is going to stay in the queue */ 02765 if (!res) { 02766 if (ringing) 02767 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02768 else 02769 ast_moh_start(qe->chan, qe->moh, NULL); 02770 } 02771 02772 /* update last_periodic_announce_time */ 02773 qe->last_periodic_announce_time = now; 02774 02775 /* Update the current periodic announcement to the next announcement */ 02776 if (!qe->parent->randomperiodicannounce) { 02777 qe->last_periodic_announce_sound++; 02778 } 02779 02780 return res; 02781 }
| static int say_position | ( | struct queue_ent * | qe, | |
| int | ringing | |||
| ) | [static] |
Definition at line 2080 of file app_queue.c.
References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, call_queue::announceposition, ANNOUNCEPOSITION_LIMIT, ANNOUNCEPOSITION_MORE_THAN, ANNOUNCEPOSITION_YES, call_queue::announcepositionlimit, AST_CONTROL_RINGING, AST_DIGIT_ANY, ast_indicate(), ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verb, queue_ent::chan, call_queue::holdtime, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, call_queue::minannouncefrequency, queue_ent::moh, ast_channel::name, call_queue::name, queue_ent::parent, play_file(), queue_ent::pos, call_queue::queue_quantity1, call_queue::queue_quantity2, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, and valid_exit().
Referenced by queue_exec(), and wait_our_turn().
02081 { 02082 int res = 0, avgholdmins, avgholdsecs, announceposition = 0; 02083 int say_thanks = 1; 02084 time_t now; 02085 02086 /* Let minannouncefrequency seconds pass between the start of each position announcement */ 02087 time(&now); 02088 if ((now - qe->last_pos) < qe->parent->minannouncefrequency) 02089 return 0; 02090 02091 /* If either our position has changed, or we are over the freq timer, say position */ 02092 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 02093 return 0; 02094 02095 if (ringing) { 02096 ast_indicate(qe->chan,-1); 02097 } else { 02098 ast_moh_stop(qe->chan); 02099 } 02100 02101 if (qe->parent->announceposition == ANNOUNCEPOSITION_YES || 02102 qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN || 02103 (qe->parent->announceposition == ANNOUNCEPOSITION_LIMIT && 02104 qe->pos <= qe->parent->announcepositionlimit)) 02105 announceposition = 1; 02106 02107 02108 if (announceposition == 1) { 02109 /* Say we're next, if we are */ 02110 if (qe->pos == 1) { 02111 res = play_file(qe->chan, qe->parent->sound_next); 02112 if (res) 02113 goto playout; 02114 else 02115 goto posout; 02116 } else { 02117 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02118 /* More than Case*/ 02119 res = play_file(qe->chan, qe->parent->queue_quantity1); 02120 if (res) 02121 goto playout; 02122 res = ast_say_number(qe->chan, qe->parent->announcepositionlimit, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02123 if (res) 02124 goto playout; 02125 } else { 02126 /* Normal Case */ 02127 res = play_file(qe->chan, qe->parent->sound_thereare); 02128 if (res) 02129 goto playout; 02130 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, NULL); /* Needs gender */ 02131 if (res) 02132 goto playout; 02133 } 02134 if (qe->parent->announceposition == ANNOUNCEPOSITION_MORE_THAN && qe->pos > qe->parent->announcepositionlimit){ 02135 /* More than Case*/ 02136 res = play_file(qe->chan, qe->parent->queue_quantity2); 02137 if (res) 02138 goto playout; 02139 } else { 02140 res = play_file(qe->chan, qe->parent->sound_calls); 02141 if (res) 02142 goto playout; 02143 } 02144 } 02145 } 02146 /* Round hold time to nearest minute */ 02147 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 02148 02149 /* If they have specified a rounding then round the seconds as well */ 02150 if (qe->parent->roundingseconds) { 02151 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 02152 avgholdsecs *= qe->parent->roundingseconds; 02153 } else { 02154 avgholdsecs = 0; 02155 } 02156 02157 ast_verb(3, "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 02158 02159 /* If the hold time is >1 min, if it's enabled, and if it's not 02160 supposed to be only once and we have already said it, say it */ 02161 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 02162 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 02163 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 02164 res = play_file(qe->chan, qe->parent->sound_holdtime); 02165 if (res) 02166 goto playout; 02167 02168 if (avgholdmins >= 1) { 02169 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 02170 if (res) 02171 goto playout; 02172 02173 if (avgholdmins == 1) { 02174 res = play_file(qe->chan, qe->parent->sound_minute); 02175 if (res) 02176 goto playout; 02177 } else { 02178 res = play_file(qe->chan, qe->parent->sound_minutes); 02179 if (res) 02180 goto playout; 02181 } 02182 } 02183 if (avgholdsecs >= 1) { 02184 res = ast_say_number(qe->chan, avgholdmins > 1 ? avgholdsecs : avgholdmins * 60 + avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 02185 if (res) 02186 goto playout; 02187 02188 res = play_file(qe->chan, qe->parent->sound_seconds); 02189 if (res) 02190 goto playout; 02191 } 02192 } else if (qe->parent->announceholdtime && !qe->parent->announceposition) { 02193 say_thanks = 0; 02194 } 02195 02196 posout: 02197 if (qe->parent->announceposition) { 02198 ast_verb(3, "Told %s in %s their queue position (which was %d)\n", 02199 qe->chan->name, qe->parent->name, qe->pos); 02200 } 02201 if (say_thanks) { 02202 res = play_file(qe->chan, qe->parent->sound_thanks); 02203 } 02204 playout: 02205 02206 if ((res > 0 && !valid_exit(qe, res))) 02207 res = 0; 02208 02209 /* Set our last_pos indicators */ 02210 qe->last_pos = now; 02211 qe->last_pos_said = qe->pos; 02212 02213 /* Don't restart music on hold if we're about to exit the caller from the queue */ 02214 if (!res) { 02215 if (ringing) { 02216 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02217 } else { 02218 ast_moh_start(qe->chan, qe->moh, NULL); 02219 } 02220 } 02221 return res; 02222 }
| 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 3383 of file app_queue.c.
References AGENT, CALLER, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::eventwhencalled, member::interface, manager_event, member::membername, ast_channel::name, queue_ent::parent, QUEUE_EVENT_VARIABLES, queue_ent::start, TRANSFER, ast_channel::uniqueid, and vars2manager().
Referenced by try_calling().
03386 { 03387 const char *reason = NULL; /* silence dumb compilers */ 03388 03389 if (!qe->parent->eventwhencalled) 03390 return; 03391 03392 switch (rsn) { 03393 case CALLER: 03394 reason = "caller"; 03395 break; 03396 case AGENT: 03397 reason = "agent"; 03398 break; 03399 case TRANSFER: 03400 reason = "transfer"; 03401 break; 03402 } 03403 03404 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03405 "Queue: %s\r\n" 03406 "Uniqueid: %s\r\n" 03407 "Channel: %s\r\n" 03408 "Member: %s\r\n" 03409 "MemberName: %s\r\n" 03410 "HoldTime: %ld\r\n" 03411 "TalkTime: %ld\r\n" 03412 "Reason: %s\r\n" 03413 "%s", 03414 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03415 (long)(callstart - qe->start), (long)(time(NULL) - callstart), reason, 03416 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : ""); 03417 }
| static int set_member_paused | ( | const char * | queuename, | |
| const char * | interface, | |||
| const char * | reason, | |||
| int | paused | |||
| ) | [static] |
Definition at line 4509 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().
04510 { 04511 int found = 0; 04512 struct call_queue *q; 04513 struct member *mem; 04514 struct ao2_iterator queue_iter; 04515 int failed; 04516 04517 /* Special event for when all queues are paused - individual events still generated */ 04518 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 04519 if (ast_strlen_zero(queuename)) 04520 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 04521 04522 queue_iter = ao2_iterator_init(queues, 0); 04523 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate over queues"))) { 04524 ao2_lock(q); 04525 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 04526 if ((mem = interface_exists(q, interface))) { 04527 if (mem->paused == paused) { 04528 ast_debug(1, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 04529 } 04530 04531 failed = 0; 04532 if (mem->realtime) { 04533 failed = update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 04534 } 04535 04536 if (failed) { 04537 ast_log(LOG_WARNING, "Failed %spausing realtime queue member %s:%s\n", (paused ? "" : "un"), q->name, interface); 04538 ao2_ref(mem, -1); 04539 ao2_unlock(q); 04540 queue_t_unref(q, "Done with iterator"); 04541 continue; 04542 } 04543 found++; 04544 mem->paused = paused; 04545 04546 if (queue_persistent_members) 04547 dump_queue_members(q); 04548 04549 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", S_OR(reason, "")); 04550 04551 if (!ast_strlen_zero(reason)) { 04552 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 04553 "Queue: %s\r\n" 04554 "Location: %s\r\n" 04555 "MemberName: %s\r\n" 04556 "Paused: %d\r\n" 04557 "Reason: %s\r\n", 04558 q->name, mem->interface, mem->membername, paused, reason); 04559 } else { 04560 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 04561 "Queue: %s\r\n" 04562 "Location: %s\r\n" 04563 "MemberName: %s\r\n" 04564 "Paused: %d\r\n", 04565 q->name, mem->interface, mem->membername, paused); 04566 } 04567 ao2_ref(mem, -1); 04568 } 04569 } 04570 04571 if (!ast_strlen_zero(queuename) && !strcasecmp(queuename, q->name)) { 04572 ao2_unlock(q); 04573 queue_t_unref(q, "Done with iterator"); 04574 break; 04575 } 04576 04577 ao2_unlock(q); 04578 queue_t_unref(q, "Done with iterator"); 04579 } 04580 ao2_iterator_destroy(&queue_iter); 04581 04582 return found ? RESULT_SUCCESS : RESULT_FAILURE; 04583 }
| static int set_member_penalty | ( | char * | queuename, | |
| char * | interface, | |||
| int | penalty | |||
| ) | [static] |
Definition at line 4586 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().
04587 { 04588 int foundinterface = 0, foundqueue = 0; 04589 struct call_queue *q; 04590 struct member *mem; 04591 struct ao2_iterator queue_iter; 04592 04593 if (penalty < 0) { 04594 ast_log(LOG_ERROR, "Invalid penalty (%d)\n", penalty); 04595 return RESULT_FAILURE; 04596 } 04597 04598 queue_iter = ao2_iterator_init(queues, 0); 04599 while ((q = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 04600 ao2_lock(q); 04601 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 04602 foundqueue++; 04603 if ((mem = interface_exists(q, interface))) { 04604 foundinterface++; 04605 mem->penalty = penalty; 04606 04607 ast_queue_log(q->name, "NONE", interface, "PENALTY", "%d", penalty); 04608 manager_event(EVENT_FLAG_AGENT, "QueueMemberPenalty", 04609 "Queue: %s\r\n" 04610 "Location: %s\r\n" 04611 "Penalty: %d\r\n", 04612 q->name, mem->interface, penalty); 04613 ao2_ref(mem, -1); 04614 } 04615 } 04616 ao2_unlock(q); 04617 queue_t_unref(q, "Done with iterator"); 04618 } 04619 ao2_iterator_destroy(&queue_iter); 04620 04621 if (foundinterface) { 04622 return RESULT_SUCCESS; 04623 } else if (!foundqueue) { 04624 ast_log (LOG_ERROR, "Invalid queuename\n"); 04625 } else { 04626 ast_log (LOG_ERROR, "Invalid interface\n"); 04627 } 04628 04629 return RESULT_FAILURE; 04630 }
| static void set_queue_result | ( | struct ast_channel * | chan, | |
| enum queue_result | res | |||
| ) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 827 of file app_queue.c.
References ARRAY_LEN, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
00828 { 00829 int i; 00830 00831 for (i = 0; i < ARRAY_LEN(queue_results); i++) { 00832 if (queue_results[i].id == res) { 00833 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 00834 return; 00835 } 00836 } 00837 }
| static void set_queue_variables | ( | struct call_queue * | q, | |
| struct ast_channel * | chan | |||
| ) | [static] |
Set variables of queue.
Definition at line 902 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, call_queue::strategy, and call_queue::talktime.
Referenced by end_bridge_callback(), queue_exec(), record_abandoned(), and try_calling().
00903 { 00904 char interfacevar[256]=""; 00905 float sl = 0; 00906 00907 if (q->setqueuevar) { 00908 sl = 0; 00909 if (q->callscompleted > 0) 00910 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 00911 00912 snprintf(interfacevar, sizeof(interfacevar), 00913 "QUEUENAME=%s,QUEUEMAX=%d,QUEUESTRATEGY=%s,QUEUECALLS=%d,QUEUEHOLDTIME=%d,QUEUETALKTIME=%d,QUEUECOMPLETED=%d,QUEUEABANDONED=%d,QUEUESRVLEVEL=%d,QUEUESRVLEVELPERF=%2.1f", 00914 q->name, q->maxlen, int2strat(q->strategy), q->count, q->holdtime, q->talktime, q->callscompleted, q->callsabandoned, q->servicelevel, sl); 00915 00916 pbx_builtin_setvar_multiple(chan, interfacevar); 00917 } 00918 }
| 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 3487 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().
03488 { 03489 struct ast_datastore *ds; 03490 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 03491 03492 if (!qtds) { 03493 ast_log(LOG_WARNING, "Memory allocation error!\n"); 03494 return NULL; 03495 } 03496 03497 ast_channel_lock(qe->chan); 03498 if (!(ds = ast_datastore_alloc(&queue_transfer_info, NULL))) { 03499 ast_channel_unlock(qe->chan); 03500 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 03501 return NULL; 03502 } 03503 03504 qtds->qe = qe; 03505 /* This member is refcounted in try_calling, so no need to add it here, too */ 03506 qtds->member = member; 03507 qtds->starttime = starttime; 03508 qtds->callcompletedinsl = callcompletedinsl; 03509 ds->data = qtds; 03510 ast_channel_datastore_add(qe->chan, ds); 03511 ast_channel_unlock(qe->chan); 03512 return ds; 03513 }
| 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 2707 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().
02708 { 02709 struct callattempt *best = find_best(outgoing); 02710 02711 if (best) { 02712 /* Ring just the best channel */ 02713 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 02714 qe->linpos = best->metric % 1000; 02715 } else { 02716 /* Just increment rrpos */ 02717 if (qe->linwrapped) { 02718 /* No more channels, start over */ 02719 qe->linpos = 0; 02720 } else { 02721 /* Prioritize next entry */ 02722 qe->linpos++; 02723 } 02724 } 02725 qe->linwrapped = 0; 02726 02727 return 0; 02728 }
| 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 2683 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().
02684 { 02685 struct callattempt *best = find_best(outgoing); 02686 02687 if (best) { 02688 /* Ring just the best channel */ 02689 ast_debug(1, "Next is '%s' with metric %d\n", best->interface, best->metric); 02690 qe->parent->rrpos = best->metric % 1000; 02691 } else { 02692 /* Just increment rrpos */ 02693 if (qe->parent->wrapped) { 02694 /* No more channels, start over */ 02695 qe->parent->rrpos = 0; 02696 } else { 02697 /* Prioritize next entry */ 02698 qe->parent->rrpos++; 02699 } 02700 } 02701 qe->parent->wrapped = 0; 02702 02703 return 0; 02704 }
| static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 851 of file app_queue.c.
References ARRAY_LEN, and strategies.
Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_single_queue().
00852 { 00853 int x; 00854 00855 for (x = 0; x < ARRAY_LEN(strategies); x++) { 00856 if (!strcasecmp(strategy, strategies[x].name)) 00857 return strategies[x].strategy; 00858 } 00859 00860 return -1; 00861 }
| 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 3569 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_FLAG_ANSWERED_ELSEWHERE, ast_free, ast_hangup(), ast_indicate(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_pbx_run_args(), ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), CALLER, member::calls, queue_ent::cancel_answered_elsewhere, 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, ast_channel::language, 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_channel::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, ast_channel::uniqueid, update_queue(), vars2manager(), wait_for_answer(), X_REC_IN, and X_REC_OUT.
Referenced by queue_exec().
03570 { 03571 struct member *cur; 03572 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 03573 int to, orig; 03574 char oldexten[AST_MAX_EXTENSION]=""; 03575 char oldcontext[AST_MAX_CONTEXT]=""; 03576 char queuename[256]=""; 03577 char interfacevar[256]=""; 03578 struct ast_channel *peer; 03579 struct ast_channel *which; 03580 struct callattempt *lpeer; 03581 struct member *member; 03582 struct ast_app *application; 03583 int res = 0, bridge = 0; 03584 int numbusies = 0; 03585 int x=0; 03586 char *announce = NULL; 03587 char digit = 0; 03588 time_t callstart; 03589 time_t now = time(NULL); 03590 struct ast_bridge_config bridge_config; 03591 char nondataquality = 1; 03592 char *agiexec = NULL; 03593 char *macroexec = NULL; 03594 char *gosubexec = NULL; 03595 int ret = 0; 03596 const char *monitorfilename; 03597 const char *monitor_exec; 03598 const char *monitor_options; 03599 char tmpid[256], tmpid2[256]; 03600 char meid[1024], meid2[1024]; 03601 char mixmonargs[1512]; 03602 struct ast_app *mixmonapp = NULL; 03603 char *p; 03604 char vars[2048]; 03605 int forwardsallowed = 1; 03606 int callcompletedinsl; 03607 struct ao2_iterator memi; 03608 struct ast_datastore *datastore, *transfer_ds; 03609 struct queue_end_bridge *queue_end_bridge = NULL; 03610 const int need_weight = use_weight; 03611 03612 ast_channel_lock(qe->chan); 03613 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 03614 ast_channel_unlock(qe->chan); 03615 03616 memset(&bridge_config, 0, sizeof(bridge_config)); 03617 tmpid[0] = 0; 03618 meid[0] = 0; 03619 time(&now); 03620 03621 /* If we've already exceeded our timeout, then just stop 03622 * This should be extremely rare. queue_exec will take care 03623 * of removing the caller and reporting the timeout as the reason. 03624 */ 03625 if (qe->expire && now >= qe->expire) { 03626 res = 0; 03627 goto out; 03628 } 03629 03630 for (; options && *options; options++) 03631 switch (*options) { 03632 case 't': 03633 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 03634 break; 03635 case 'T': 03636 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 03637 break; 03638 case 'w': 03639 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 03640 break; 03641 case 'W': 03642 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 03643 break; 03644 case 'c': 03645 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN); 03646 break; 03647 case 'd': 03648 nondataquality = 0; 03649 break; 03650 case 'h': 03651 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 03652 break; 03653 case 'H': 03654 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 03655 break; 03656 case 'k': 03657 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_PARKCALL); 03658 break; 03659 case 'K': 03660 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_PARKCALL); 03661 break; 03662 case 'n': 03663 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_LINEAR) 03664 (*tries)++; 03665 else 03666 *tries = qe->parent->membercount; 03667 *noption = 1; 03668 break; 03669 case 'i': 03670 forwardsallowed = 0; 03671 break; 03672 case 'x': 03673 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMIXMON); 03674 break; 03675 case 'X': 03676 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMIXMON); 03677 break; 03678 case 'C': 03679 qe->cancel_answered_elsewhere = 1; 03680 break; 03681 03682 } 03683 03684 /* if the calling channel has the ANSWERED_ELSEWHERE flag set, make sure this is inherited. 03685 (this is mainly to support chan_local) 03686 */ 03687 if (ast_test_flag(qe->chan, AST_FLAG_ANSWERED_ELSEWHERE)) { 03688 qe->cancel_answered_elsewhere = 1; 03689 } 03690 03691 /* Hold the lock while we setup the outgoing calls */ 03692 if (need_weight) 03693 ao2_lock(queues); 03694 ao2_lock(qe->parent); 03695 ast_debug(1, "%s is trying to call a queue member.\n", 03696 qe->chan->name); 03697 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 03698 if (!ast_strlen_zero(qe->announce)) 03699 announce = qe->announce; 03700 if (!ast_strlen_zero(announceoverride)) 03701 announce = announceoverride; 03702 03703 memi = ao2_iterator_init(qe->parent->members, 0); 03704 while ((cur = ao2_iterator_next(&memi))) { 03705 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 03706 struct ast_dialed_interface *di; 03707 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 03708 if (!tmp) { 03709 ao2_ref(cur, -1); 03710 ao2_unlock(qe->parent); 03711 ao2_iterator_destroy(&memi); 03712 if (need_weight) 03713 ao2_unlock(queues); 03714 goto out; 03715 } 03716 if (!datastore) { 03717 if (!(datastore = ast_datastore_alloc(&dialed_interface_info, NULL))) { 03718 ao2_ref(cur, -1); 03719 ao2_unlock(qe->parent); 03720 ao2_iterator_destroy(&memi); 03721 if (need_weight) 03722 ao2_unlock(queues); 03723 free(tmp); 03724 goto out; 03725 } 03726 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 03727 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 03728 ao2_ref(cur, -1); 03729 ao2_unlock(&qe->parent); 03730 ao2_iterator_destroy(&memi); 03731 if (need_weight) 03732 ao2_unlock(queues); 03733 free(tmp); 03734 goto out; 03735 } 03736 datastore->data = dialed_interfaces; 03737 AST_LIST_HEAD_INIT(dialed_interfaces); 03738 03739 ast_channel_lock(qe->chan); 03740 ast_channel_datastore_add(qe->chan, datastore); 03741 ast_channel_unlock(qe->chan); 03742 } else 03743 dialed_interfaces = datastore->data; 03744 03745 AST_LIST_LOCK(dialed_interfaces); 03746 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 03747 if (!strcasecmp(cur->interface, di->interface)) { 03748 ast_debug(1, "Skipping dialing interface '%s' since it has already been dialed\n", 03749 di->interface); 03750 break; 03751 } 03752 } 03753 AST_LIST_UNLOCK(dialed_interfaces); 03754 03755 if (di) { 03756 free(tmp); 03757 continue; 03758 } 03759 03760 /* It is always ok to dial a Local interface. We only keep track of 03761 * which "real" interfaces have been dialed. The Local channel will 03762 * inherit this list so that if it ends up dialing a real interface, 03763 * it won't call one that has already been called. */ 03764 if (strncasecmp(cur->interface, "Local/", 6)) { 03765 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 03766 ao2_ref(cur, -1); 03767 ao2_unlock(qe->parent); 03768 ao2_iterator_destroy(&memi); 03769 if (need_weight) 03770 ao2_unlock(queues); 03771 free(tmp); 03772 goto out; 03773 } 03774 strcpy(di->interface, cur->interface); 03775 03776 AST_LIST_LOCK(dialed_interfaces); 03777 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 03778 AST_LIST_UNLOCK(dialed_interfaces); 03779 } 03780 03781 tmp->stillgoing = -1; 03782 tmp->member = cur; 03783 tmp->oldstatus = cur->status; 03784 tmp->lastcall = cur->lastcall; 03785 tmp->lastqueue = cur->lastqueue; 03786 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 03787 /* Special case: If we ring everyone, go ahead and ring them, otherwise 03788 just calculate their metric for the appropriate strategy */ 03789 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 03790 /* Put them in the list of outgoing thingies... We're ready now. 03791 XXX If we're forcibly removed, these outgoing calls won't get 03792 hung up XXX */ 03793 tmp->q_next = outgoing; 03794 outgoing = tmp; 03795 /* If this line is up, don't try anybody else */ 03796 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 03797 break; 03798 } else { 03799 ao2_ref(cur, -1); 03800 ast_free(tmp); 03801 } 03802 } 03803 ao2_iterator_destroy(&memi); 03804 03805 if (qe->parent->timeoutpriority == TIMEOUT_PRIORITY_APP) { 03806 /* Application arguments have higher timeout priority (behaviour for <=1.6) */ 03807 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 03808 to = (qe->expire - now) * 1000; 03809 else 03810 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 03811 } else { 03812 /* Config timeout is higher priority thatn application timeout */ 03813 if (qe->expire && qe->expire<=now) { 03814 to = 0; 03815 } else if (qe->parent->timeout) { 03816 to = qe->parent->timeout * 1000; 03817 } else { 03818 to = -1; 03819 } 03820 } 03821 orig = to; 03822 ++qe->pending; 03823 ao2_unlock(qe->parent); 03824 ring_one(qe, outgoing, &numbusies); 03825 if (need_weight) 03826 ao2_unlock(queues); 03827 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); 03828 /* The ast_channel_datastore_remove() function could fail here if the 03829 * datastore was moved to another channel during a masquerade. If this is 03830 * the case, don't free the datastore here because later, when the channel 03831 * to which the datastore was moved hangs up, it will attempt to free this 03832 * datastore again, causing a crash 03833 */ 03834 ast_channel_lock(qe->chan); 03835 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 03836 ast_datastore_free(datastore); 03837 } 03838 ast_channel_unlock(qe->chan); 03839 ao2_lock(qe->parent); 03840 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { 03841 store_next_rr(qe, outgoing); 03842 } 03843 if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) { 03844 store_next_lin(qe, outgoing); 03845 } 03846 ao2_unlock(qe->parent); 03847 peer = lpeer ? lpeer->chan : NULL; 03848 if (!peer) { 03849 qe->pending = 0; 03850 if (to) { 03851 /* Must gotten hung up */ 03852 res = -1; 03853 } else { 03854 /* User exited by pressing a digit */ 03855 res = digit; 03856 } 03857 if (res == -1) 03858 ast_debug(1, "%s: Nobody answered.\n", qe->chan->name); 03859 if (ast_cdr_isset_unanswered()) { 03860 /* channel contains the name of one of the outgoing channels 03861 in its CDR; zero out this CDR to avoid a dual-posting */ 03862 struct callattempt *o; 03863 for (o = outgoing; o; o = o->q_next) { 03864 if (!o->chan) { 03865 continue; 03866 } 03867 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) { 03868 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED); 03869 break; 03870 } 03871 } 03872 } 03873 } else { /* peer is valid */ 03874 /* Ah ha! Someone answered within the desired timeframe. Of course after this 03875 we will always return with -1 so that it is hung up properly after the 03876 conversation. */ 03877 if (!strcmp(qe->chan->tech->type, "DAHDI")) 03878 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 03879 if (!strcmp(peer->tech->type, "DAHDI")) 03880 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 03881 /* Update parameters for the queue */ 03882 time(&now); 03883 recalc_holdtime(qe, (now - qe->start)); 03884 ao2_lock(qe->parent); 03885 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 03886 ao2_unlock(qe->parent); 03887 member = lpeer->member; 03888 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 03889 ao2_ref(member, 1); 03890 hangupcalls(outgoing, peer, qe->cancel_answered_elsewhere); 03891 outgoing = NULL; 03892 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 03893 int res2; 03894 03895 res2 = ast_autoservice_start(qe->chan); 03896 if (!res2) { 03897 if (qe->parent->memberdelay) { 03898 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 03899 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 03900 } 03901 if (!res2 && announce) { 03902 play_file(peer, announce); 03903 } 03904 if (!res2 && qe->parent->reportholdtime) { 03905 if (!play_file(peer, qe->parent->sound_reporthold)) { 03906 int holdtime, holdtimesecs; 03907 03908 time(&now); 03909 holdtime = abs((now - qe->start) / 60); 03910 holdtimesecs = abs((now - qe->start) % 60); 03911 if (holdtime > 0) { 03912 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 03913 play_file(peer, qe->parent->sound_minutes); 03914 } 03915 if (holdtimesecs > 1) { 03916 ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL); 03917 play_file(peer, qe->parent->sound_seconds); 03918 } 03919 } 03920 } 03921 } 03922 res2 |= ast_autoservice_stop(qe->chan); 03923 if (ast_check_hangup(peer)) { 03924 /* Agent must have hung up */ 03925 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 03926 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 03927 if (qe->parent->eventwhencalled) 03928 manager_event(EVENT_FLAG_AGENT, "AgentDump", 03929 "Queue: %s\r\n" 03930 "Uniqueid: %s\r\n" 03931 "Channel: %s\r\n" 03932 "Member: %s\r\n" 03933 "MemberName: %s\r\n" 03934 "%s", 03935 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03936 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03937 ast_hangup(peer); 03938 ao2_ref(member, -1); 03939 goto out; 03940 } else if (res2) { 03941 /* Caller must have hung up just before being connected*/ 03942 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 03943 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 03944 record_abandoned(qe); 03945 ast_cdr_noanswer(qe->chan->cdr); 03946 ast_hangup(peer); 03947 ao2_ref(member, -1); 03948 return -1; 03949 } 03950 } 03951 /* Stop music on hold */ 03952 if (ringing) 03953 ast_indicate(qe->chan,-1); 03954 else 03955 ast_moh_stop(qe->chan); 03956 /* If appropriate, log that we have a destination channel */ 03957 if (qe->chan->cdr) 03958 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 03959 /* Make sure channels are compatible */ 03960 res = ast_channel_make_compatible(qe->chan, peer); 03961 if (res < 0) { 03962 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 03963 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 03964 record_abandoned(qe); 03965 ast_cdr_failed(qe->chan->cdr); 03966 ast_hangup(peer); 03967 ao2_ref(member, -1); 03968 return -1; 03969 } 03970 03971 /* Play announcement to the caller telling it's his turn if defined */ 03972 if (!ast_strlen_zero(qe->parent->sound_callerannounce)) { 03973 if (play_file(qe->chan, qe->parent->sound_callerannounce)) 03974 ast_log(LOG_WARNING, "Announcement file '%s' is unavailable, continuing anyway...\n", qe->parent->sound_callerannounce); 03975 } 03976 03977 ao2_lock(qe->parent); 03978 /* if setinterfacevar is defined, make member variables available to the channel */ 03979 /* use pbx_builtin_setvar to set a load of variables with one call */ 03980 if (qe->parent->setinterfacevar) { 03981 snprintf(interfacevar, sizeof(interfacevar), "MEMBERINTERFACE=%s,MEMBERNAME=%s,MEMBERCALLS=%d,MEMBERLASTCALL=%ld,MEMBERPENALTY=%d,MEMBERDYNAMIC=%d,MEMBERREALTIME=%d", 03982 member->interface, member->membername, member->calls, (long)member->lastcall, member->penalty, member->dynamic, member->realtime); 03983 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 03984 pbx_builtin_setvar_multiple(peer, interfacevar); 03985 } 03986 03987 /* if setqueueentryvar is defined, make queue entry (i.e. the caller) variables available to the channel */ 03988 /* use pbx_builtin_setvar to set a load of variables with one call */ 03989 if (qe->parent->setqueueentryvar) { 03990 snprintf(interfacevar, sizeof(interfacevar), "QEHOLDTIME=%ld,QEORIGINALPOS=%d", 03991 (long) time(NULL) - qe->start, qe->opos); 03992 pbx_builtin_setvar_multiple(qe->chan, interfacevar); 03993 pbx_builtin_setvar_multiple(peer, interfacevar); 03994 } 03995 03996 /* try to set queue variables if configured to do so*/ 03997 set_queue_variables(qe->parent, qe->chan); 03998 set_queue_variables(qe->parent, peer); 03999 ao2_unlock(qe->parent); 04000 04001 ast_channel_lock(qe->chan); 04002 if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) { 04003 monitorfilename = ast_strdupa(monitorfilename); 04004 } 04005 ast_channel_unlock(qe->chan); 04006 /* Begin Monitoring */ 04007 if (qe->parent->monfmt && *qe->parent->monfmt) { 04008 if (!qe->parent->montype) { 04009 const char *monexec, *monargs; 04010 ast_debug(1, "Starting Monitor as requested.\n"); 04011 ast_channel_lock(qe->chan); 04012 if ((monexec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC")) || (monargs = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))) { 04013 which = qe->chan; 04014 monexec = monexec ? ast_strdupa(monexec) : NULL; 04015 } 04016 else 04017 which = peer; 04018 ast_channel_unlock(qe->chan); 04019 if (ast_monitor_start) { 04020 if (monitorfilename) { 04021 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); 04022 } else if (qe->chan->cdr && ast_monitor_start) { 04023 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1, X_REC_IN | X_REC_OUT); 04024 } else if (ast_monitor_start) { 04025 /* Last ditch effort -- no CDR, make up something */ 04026 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 04027 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); 04028 } 04029 } 04030 if (!ast_strlen_zero(monexec) && ast_monitor_setjoinfiles) { 04031 ast_monitor_setjoinfiles(which, 1); 04032 } 04033 } else { 04034 mixmonapp = pbx_findapp("MixMonitor"); 04035 04036 if (mixmonapp) { 04037 ast_debug(1, "Starting MixMonitor as requested.\n"); 04038 if (!monitorfilename) { 04039 if (qe->chan->cdr) 04040 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)); 04041 else 04042 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 04043 } else { 04044 const char *m = monitorfilename; 04045 for (p = tmpid2; p < tmpid2 + sizeof(tmpid2) - 1; p++, m++) { 04046 switch (*m) { 04047 case '^': 04048 if (*(m + 1) == '{') 04049 *p = '$'; 04050 break; 04051 case ',': 04052 *p++ = '\\'; 04053 /* Fall through */ 04054 default: 04055 *p = *m; 04056 } 04057 if (*m == '\0') 04058 break; 04059 } 04060 if (p == tmpid2 + sizeof(tmpid2)) 04061 tmpid2[sizeof(tmpid2) - 1] = '\0'; 04062 04063 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 04064 } 04065 04066 ast_channel_lock(qe->chan); 04067 if ((monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"))) { 04068 monitor_exec = ast_strdupa(monitor_exec); 04069 } 04070 if ((monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"))) { 04071 monitor_options = ast_strdupa(monitor_options); 04072 } else { 04073 monitor_options = ""; 04074 } 04075 ast_channel_unlock(qe->chan); 04076 04077 if (monitor_exec) { 04078 const char *m = monitor_exec; 04079 for (p = meid2; p < meid2 + sizeof(meid2) - 1; p++, m++) { 04080 switch (*m) { 04081 case '^': 04082 if (*(m + 1) == '{') 04083 *p = '$'; 04084 break; 04085 case ',': 04086 *p++ = '\\'; 04087 /* Fall through */ 04088 default: 04089 *p = *m; 04090 } 04091 if (*m == '\0') 04092 break; 04093 } 04094 if (p == meid2 + sizeof(meid2)) 04095 meid2[sizeof(meid2) - 1] = '\0'; 04096 04097 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 04098 } 04099 04100 snprintf(tmpid2, sizeof(tmpid2), "%s.%s", tmpid, qe->parent->monfmt); 04101 04102 if (!ast_strlen_zero(monitor_exec)) 04103 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s,%s", tmpid2, monitor_options, monitor_exec); 04104 else 04105 snprintf(mixmonargs, sizeof(mixmonargs), "%s,b%s", tmpid2, monitor_options); 04106 04107 ast_debug(1, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 04108 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 04109 if (qe->chan->cdr) 04110 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 04111 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 04112 if (qe->chan->cdr) 04113 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 04114 04115 } else { 04116 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 04117 } 04118 } 04119 } 04120 /* Drop out of the queue at this point, to prepare for next caller */ 04121 leave_queue(qe); 04122 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 04123 ast_debug(1, "app_queue: sendurl=%s.\n", url); 04124 ast_channel_sendurl(peer, url); 04125 } 04126 04127 /* run a macro for this connection if defined. The macro simply returns, no action is taken on the result */ 04128 /* use macro from dialplan if passed as a option, otherwise use the default queue macro */ 04129 if (!ast_strlen_zero(macro)) { 04130 macroexec = ast_strdupa(macro); 04131 } else { 04132 if (qe->parent->membermacro) 04133 macroexec = ast_strdupa(qe->parent->membermacro); 04134 } 04135 04136 if (!ast_strlen_zero(macroexec)) { 04137 ast_debug(1, "app_queue: macro=%s.\n", macroexec); 04138 04139 res = ast_autoservice_start(qe->chan); 04140 if (res) { 04141 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 04142 res = -1; 04143 } 04144 04145 application = pbx_findapp("Macro"); 04146 04147 if (application) { 04148 res = pbx_exec(peer, application, macroexec); 04149 ast_debug(1, "Macro exited with status %d\n", res); 04150 res = 0; 04151 } else { 04152 ast_log(LOG_ERROR, "Could not find application Macro\n"); 04153 res = -1; 04154 } 04155 04156 if (ast_autoservice_stop(qe->chan) < 0) { 04157 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 04158 res = -1; 04159 } 04160 } 04161 04162 /* run a gosub for this connection if defined. The gosub simply returns, no action is taken on the result */ 04163 /* use gosub from dialplan if passed as a option, otherwise use the default queue gosub */ 04164 if (!ast_strlen_zero(gosub)) { 04165 gosubexec = ast_strdupa(gosub); 04166 } else { 04167 if (qe->parent->membergosub) 04168 gosubexec = ast_strdupa(qe->parent->membergosub); 04169 } 04170 04171 if (!ast_strlen_zero(gosubexec)) { 04172 ast_debug(1, "app_queue: gosub=%s.\n", gosubexec); 04173 04174 res = ast_autoservice_start(qe->chan); 04175 if (res) { 04176 ast_log(LOG_ERROR, "Unable to start autoservice on calling channel\n"); 04177 res = -1; 04178 } 04179 04180 application = pbx_findapp("Gosub"); 04181 04182 if (application) { 04183 char *gosub_args, *gosub_argstart; 04184 04185 /* Set where we came from */ 04186 ast_copy_string(peer->context, "app_queue_gosub_virtual_context", sizeof(peer->context)); 04187 ast_copy_string(peer->exten, "s", sizeof(peer->exten)); 04188 peer->priority = 0; 04189 04190 gosub_argstart = strchr(gosubexec, ','); 04191 if (gosub_argstart) { 04192 *gosub_argstart = 0; 04193 if (asprintf(&gosub_args, "%s,s,1(%s)", gosubexec, gosub_argstart + 1) < 0) { 04194 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 04195 gosub_args = NULL; 04196 } 04197 *gosub_argstart = ','; 04198 } else { 04199 if (asprintf(&gosub_args, "%s,s,1", gosubexec) < 0) { 04200 ast_log(LOG_WARNING, "asprintf() failed: %s\n", strerror(errno)); 04201 gosub_args = NULL; 04202 } 04203 } 04204 if (gosub_args) { 04205 res = pbx_exec(peer, application, gosub_args); 04206 if (!res) { 04207 struct ast_pbx_args args; 04208 memset(&args, 0, sizeof(args)); 04209 args.no_hangup_chan = 1; 04210 ast_pbx_run_args(peer, &args); 04211 } 04212 ast_free(gosub_args); 04213 ast_debug(1, "Gosub exited with status %d\n", res); 04214 } else { 04215 ast_log(LOG_ERROR, "Could not Allocate string for Gosub arguments -- Gosub Call Aborted!\n"); 04216 } 04217 } else { 04218 ast_log(LOG_ERROR, "Could not find application Gosub\n"); 04219 res = -1; 04220 } 04221 04222 if (ast_autoservice_stop(qe->chan) < 0) { 04223 ast_log(LOG_ERROR, "Could not stop autoservice on calling channel\n"); 04224 res = -1; 04225 } 04226 } 04227 04228 if (!ast_strlen_zero(agi)) { 04229 ast_debug(1, "app_queue: agi=%s.\n", agi); 04230 application = pbx_findapp("agi"); 04231 if (application) { 04232 agiexec = ast_strdupa(agi); 04233 ret = pbx_exec(qe->chan, application, agiexec); 04234 } else 04235 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 04236 } 04237 qe->handled++; 04238 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s|%ld", (long) time(NULL) - qe->start, peer->uniqueid, 04239 (long)(orig - to > 0 ? (orig - to) / 1000 : 0)); 04240 if (update_cdr && qe->chan->cdr) 04241 ast_copy_string(qe->chan->cdr->dstchannel, member->membername, sizeof(qe->chan->cdr->dstchannel)); 04242 if (qe->parent->eventwhencalled) 04243 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 04244 "Queue: %s\r\n" 04245 "Uniqueid: %s\r\n" 04246 "Channel: %s\r\n" 04247 "Member: %s\r\n" 04248 "MemberName: %s\r\n" 04249 "Holdtime: %ld\r\n" 04250 "BridgedChannel: %s\r\n" 04251 "Ringtime: %ld\r\n" 04252 "%s", 04253 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 04254 (long) time(NULL) - qe->start, peer->uniqueid, (long)(orig - to > 0 ? (orig - to) / 1000 : 0), 04255 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 04256 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 04257 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 04258 04259 if ((queue_end_bridge = ao2_alloc(sizeof(*queue_end_bridge), NULL))) { 04260 queue_end_bridge->q = qe->parent; 04261 queue_end_bridge->chan = qe->chan; 04262 bridge_config.end_bridge_callback = end_bridge_callback; 04263 bridge_config.end_bridge_callback_data = queue_end_bridge; 04264 bridge_config.end_bridge_callback_data_fixup = end_bridge_callback_data_fixup; 04265 /* Since queue_end_bridge can survive beyond the life of this call to Queue, we need 04266 * to make sure to increase the refcount of this queue so it cannot be freed until we 04267 * are done with it. We remove this reference in end_bridge_callback. 04268 */ 04269 queue_t_ref(qe->parent, "For bridge_config reference"); 04270 } 04271 04272 time(&callstart); 04273 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 04274 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 04275 04276 /* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log 04277 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary 04278 */ 04279 ast_channel_lock(qe->chan); 04280 if (!attended_transfer_occurred(qe->chan)) { 04281 struct ast_datastore *tds; 04282 04283 /* detect a blind transfer */ 04284 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) { 04285 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld|%d", 04286 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 04287 (long) (time(NULL) - callstart), qe->opos); 04288 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER); 04289 } else if (ast_check_hangup(qe->chan)) { 04290 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 04291 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 04292 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), CALLER); 04293 } else { 04294 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 04295 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 04296 send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), AGENT); 04297 } 04298 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 04299 ast_channel_datastore_remove(qe->chan, tds); 04300 } 04301 update_queue(qe->parent, member, callcompletedinsl, (time(NULL) - callstart)); 04302 } 04303 04304 if (transfer_ds) { 04305 ast_datastore_free(transfer_ds); 04306 } 04307 ast_channel_unlock(qe->chan); 04308 ast_hangup(peer); 04309 res = bridge ? bridge : 1; 04310 ao2_ref(member, -1); 04311 } 04312 out: 04313 hangupcalls(outgoing, NULL, qe->cancel_answered_elsewhere); 04314 04315 return res; 04316 }
| static int unload_module | ( | void | ) | [static] |
Definition at line 7170 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_ref, ao2_t_iterator_next, ARRAY_LEN, ast_cli_unregister_multiple(), ast_context_destroy(), ast_context_find(), ast_context_remove_extension2(), ast_custom_function_unregister(), ast_event_unsubscribe(), ast_manager_unregister(), ast_taskprocessor_unreference(), ast_unload_realtime(), ast_unregister_application(), cli_queue, queue_t_unref, queuemembercount_dep, queuemembercount_function, queuememberlist_function, queuememberpenalty_function, queues, queues_t_unlink, queuevar_function, and queuewaitingcount_function.
07171 { 07172 int res; 07173 struct ast_context *con; 07174 struct ao2_iterator q_iter; 07175 struct call_queue *q = NULL; 07176 07177 ast_cli_unregister_multiple(cli_queue, ARRAY_LEN(cli_queue)); 07178 res = ast_manager_unregister("QueueStatus"); 07179 res |= ast_manager_unregister("Queues"); 07180 res |= ast_manager_unregister("QueueRule"); 07181 res |= ast_manager_unregister("QueueSummary"); 07182 res |= ast_manager_unregister("QueueAdd"); 07183 res |= ast_manager_unregister("QueueRemove"); 07184 res |= ast_manager_unregister("QueuePause"); 07185 res |= ast_manager_unregister("QueueLog"); 07186 res |= ast_manager_unregister("QueuePenalty"); 07187 res |= ast_unregister_application(app_aqm); 07188 res |= ast_unregister_application(app_rqm); 07189 res |= ast_unregister_application(app_pqm); 07190 res |= ast_unregister_application(app_upqm); 07191 res |= ast_unregister_application(app_ql); 07192 res |= ast_unregister_application(app); 07193 res |= ast_custom_function_unregister(&queuevar_function); 07194 res |= ast_custom_function_unregister(&queuemembercount_function); 07195 res |= ast_custom_function_unregister(&queuemembercount_dep); 07196 res |= ast_custom_function_unregister(&queuememberlist_function); 07197 res |= ast_custom_function_unregister(&queuewaitingcount_function); 07198 res |= ast_custom_function_unregister(&queuememberpenalty_function); 07199 07200 if (device_state_sub) 07201 ast_event_unsubscribe(device_state_sub); 07202 07203 if ((con = ast_context_find("app_queue_gosub_virtual_context"))) { 07204 ast_context_remove_extension2(con, "s", 1, NULL, 0); 07205 ast_context_destroy(con, "app_queue"); /* leave no trace */ 07206 } 07207 07208 q_iter = ao2_iterator_init(queues, 0); 07209 while ((q = ao2_t_iterator_next(&q_iter, "Iterate through queues"))) { 07210 queues_t_unlink(queues, q, "Remove queue from container due to unload"); 07211 queue_t_unref(q, "Done with iterator"); 07212 } 07213 ao2_iterator_destroy(&q_iter); 07214 ao2_ref(queues, -1); 07215 devicestate_tps = ast_taskprocessor_unreference(devicestate_tps); 07216 ast_unload_realtime("queue_members"); 07217 return res; 07218 }
| 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 3163 of file app_queue.c.
References ast_debug, AST_LIST_NEXT, queue_ent::chan, queue_ent::max_penalty, penalty_rule::max_relative, penalty_rule::max_value, queue_ent::min_penalty, penalty_rule::min_relative, penalty_rule::min_value, ast_channel::name, pbx_builtin_setvar_helper(), queue_ent::pr, and penalty_rule::time.
Referenced by queue_exec(), and wait_our_turn().
03164 { 03165 int max_penalty = qe->pr->max_relative ? qe->max_penalty + qe->pr->max_value : qe->pr->max_value; 03166 int min_penalty = qe->pr->min_relative ? qe->min_penalty + qe->pr->min_value : qe->pr->min_value; 03167 char max_penalty_str[20], min_penalty_str[20]; 03168 /* a relative change to the penalty could put it below 0 */ 03169 if (max_penalty < 0) 03170 max_penalty = 0; 03171 if (min_penalty < 0) 03172 min_penalty = 0; 03173 if (min_penalty > max_penalty) 03174 min_penalty = max_penalty; 03175 snprintf(max_penalty_str, sizeof(max_penalty_str), "%d", max_penalty); 03176 snprintf(min_penalty_str, sizeof(min_penalty_str), "%d", min_penalty); 03177 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MAX_PENALTY", max_penalty_str); 03178 pbx_builtin_setvar_helper(qe->chan, "QUEUE_MIN_PENALTY", min_penalty_str); 03179 qe->max_penalty = max_penalty; 03180 qe->min_penalty = min_penalty; 03181 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); 03182 qe->pr = AST_LIST_NEXT(qe->pr, list); 03183 }
| static int update_queue | ( | struct call_queue * | q, | |
| struct member * | member, | |||
| int | callcompletedinsl, | |||
| int | newtalktime | |||
| ) | [static] |
update the queue status
| Always | 0 |
Definition at line 3271 of file app_queue.c.
References ao2_find, ao2_iterator_destroy(), ao2_iterator_init(), ao2_lock(), ao2_ref, ao2_t_iterator_next, ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, call_queue::members, OBJ_POINTER, queue_t_unref, queues, and call_queue::talktime.
Referenced by queue_transfer_fixup(), and try_calling().
03272 { 03273 int oldtalktime; 03274 03275 struct member *mem; 03276 struct call_queue *qtmp; 03277 struct ao2_iterator queue_iter; 03278 03279 if (shared_lastcall) { 03280 queue_iter = ao2_iterator_init(queues, 0); 03281 while ((qtmp = ao2_t_iterator_next(&queue_iter, "Iterate through queues"))) { 03282 ao2_lock(qtmp); 03283 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { 03284 time(&mem->lastcall); 03285 mem->calls++; 03286 mem->lastqueue = q; 03287 ao2_ref(mem, -1); 03288 } 03289 ao2_unlock(qtmp); 03290 queue_t_unref(qtmp, "Done with iterator"); 03291 } 03292 ao2_iterator_destroy(&queue_iter); 03293 } else { 03294 ao2_lock(q); 03295 time(&member->lastcall); 03296 member->calls++; 03297 member->lastqueue = q; 03298 ao2_unlock(q); 03299 } 03300 ao2_lock(q); 03301 q->callscompleted++; 03302 if (callcompletedinsl) 03303 q->callscompletedinsl++; 03304 /* Calculate talktime using the same exponential average as holdtime code*/ 03305 oldtalktime = q->talktime; 03306 q->talktime = (((oldtalktime << 2) - oldtalktime) + newtalktime) >> 2; 03307 ao2_unlock(q); 03308 return 0; 03309 }
| static int update_realtime_member_field | ( | struct member * | mem, | |
| const char * | queue_name, | |||
| const char * | field, | |||
| const char * | value | |||
| ) | [static] |
Definition at line 1892 of file app_queue.c.
References ast_strlen_zero(), ast_update_realtime(), member::rt_uniqueid, and SENTINEL.
Referenced by set_member_paused().
01893 { 01894 int ret = -1; 01895 01896 if (ast_strlen_zero(mem->rt_uniqueid)) 01897 return ret; 01898 01899 if ((ast_update_realtime("queue_members", "uniqueid", mem->rt_uniqueid, field, value, SENTINEL)) > 0) 01900 ret = 0; 01901 01902 return ret; 01903 }
| static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 1906 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next, ao2_lock(), ao2_ref, ao2_unlink, ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_debug, ast_load_realtime_multientry(), ast_queue_log(), ast_variable_retrieve(), member::dead, member::interface, call_queue::membercount, call_queue::members, call_queue::name, queues, member::realtime, rt_handle_member_record(), S_OR, and SENTINEL.
Referenced by load_realtime_queue(), and queue_exec().
01907 { 01908 struct ast_config *member_config = NULL; 01909 struct member *m; 01910 char *interface = NULL; 01911 struct ao2_iterator mem_iter; 01912 01913 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , SENTINEL))) { 01914 /*This queue doesn't have realtime members*/ 01915 ast_debug(3, "Queue %s has no realtime members defined. No need for update\n", q->name); 01916 return; 01917 } 01918 01919 ao2_lock(queues); 01920 ao2_lock(q); 01921 01922 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 01923 mem_iter = ao2_iterator_init(q->members, 0); 01924 while ((m = ao2_iterator_next(&mem_iter))) { 01925 if (m->realtime) 01926 m->dead = 1; 01927 ao2_ref(m, -1); 01928 } 01929 ao2_iterator_destroy(&mem_iter); 01930 01931 while ((interface = ast_category_browse(member_config, interface))) { 01932 rt_handle_member_record(q, interface, 01933 ast_variable_retrieve(member_config, interface, "uniqueid"), 01934 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 01935 ast_variable_retrieve(member_config, interface, "penalty"), 01936 ast_variable_retrieve(member_config, interface, "paused"), 01937 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface)); 01938 } 01939 01940 /* Delete all realtime members that have been deleted in DB. */ 01941 mem_iter = ao2_iterator_init(q->members, 0); 01942 while ((m = ao2_iterator_next(&mem_iter))) { 01943 if (m->dead) { 01944 ast_queue_log(q->name, "REALTIME", m->interface, "REMOVEMEMBER", "%s", ""); 01945 ao2_unlink(q->members, m); 01946 q->membercount--; 01947 } 01948 ao2_ref(m, -1); 01949 } 01950 ao2_iterator_destroy(&mem_iter); 01951 ao2_unlock(q); 01952 ao2_unlock(queues); 01953 ast_config_destroy(member_config); 01954 }
| static int update_status | ( | struct call_queue * | q, | |
| struct member * | m, | |||
| const int | status | |||
| ) | [static] |
set a member's status based on device state of that member's state_interface.
Lock interface list find sc, iterate through each queues queue_member list for member to update state inside queues
Definition at line 1021 of file app_queue.c.
References EVENT_FLAG_AGENT, and manager_event.
Referenced by handle_statechange(), and ring_entry().
01022 { 01023 m->status = status; 01024 01025 if (q->maskmemberstatus) 01026 return 0; 01027 01028 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 01029 "Queue: %s\r\n" 01030 "Location: %s\r\n" 01031 "MemberName: %s\r\n" 01032 "Membership: %s\r\n" 01033 "Penalty: %d\r\n" 01034 "CallsTaken: %d\r\n" 01035 "LastCall: %d\r\n" 01036 "Status: %d\r\n" 01037 "Paused: %d\r\n", 01038 q->name, m->interface, m->membername, m->dynamic ? "dynamic" : m->realtime ? "realtime" : "static", 01039 m->penalty, m->calls, (int)m->lastcall, m->status, m->paused 01040 ); 01041 01042 return 0; 01043 }
| static int upqm_exec | ( | struct ast_channel * | chan, | |
| void * | data | |||
| ) | [static] |
UnPauseQueueMember application.
Definition at line 4800 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
04801 { 04802 char *parse; 04803 AST_DECLARE_APP_ARGS(args, 04804 AST_APP_ARG(queuename); 04805 AST_APP_ARG(interface); 04806 AST_APP_ARG(options); 04807 AST_APP_ARG(reason); 04808 ); 04809 04810 if (ast_strlen_zero(data)) { 04811 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename],interface[,options[,reason]])\n"); 04812 return -1; 04813 } 04814 04815 parse = ast_strdupa(data); 04816 04817 AST_STANDARD_APP_ARGS(args, parse); 04818 04819 if (ast_strlen_zero(args.interface)) { 04820 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename],interface[,options[,reason]])\n"); 04821 return -1; 04822 } 04823 04824 if (set_member_paused(args.queuename, args.interface, args.reason, 0)) { 04825 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 04826 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 04827 return 0; 04828 } 04829 04830 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 04831 04832 return 0; 04833 }
| 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 2047 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().
02048 { 02049 int digitlen = strlen(qe->digits); 02050 02051 /* Prevent possible buffer overflow */ 02052 if (digitlen < sizeof(qe->digits) - 2) { 02053 qe->digits[digitlen] = digit; 02054 qe->digits[digitlen + 1] = '\0'; 02055 } else { 02056 qe->digits[0] = '\0'; 02057 return 0; 02058 } 02059 02060 /* If there's no context to goto, short-circuit */ 02061 if (ast_strlen_zero(qe->context)) 02062 return 0; 02063 02064 /* If the extension is bad, then reset the digits to blank */ 02065 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) { 02066 qe->digits[0] = '\0'; 02067 return 0; 02068 } 02069 02070 /* We have an exact match */ 02071 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 02072 qe->valid_digits = 1; 02073 /* Return 1 on a successful goto */ 02074 return 1; 02075 } 02076 02077 return 0; 02078 }
| 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 2416 of file app_queue.c.
References ast_copy_string(), ast_str_buffer(), ast_str_thread_get(), buf, and pbx_builtin_serialize_variables().
Referenced by ring_entry(), rna(), send_agent_complete(), and try_calling().
02417 { 02418 struct ast_str *buf = ast_str_thread_get(&ast_str_thread_global_buf, len + 1); 02419 char *tmp; 02420 02421 if (pbx_builtin_serialize_variables(chan, &buf)) { 02422 int i, j; 02423 02424 /* convert "\n" to "\nVariable: " */ 02425 strcpy(vars, "Variable: "); 02426 tmp = ast_str_buffer(buf); 02427 02428 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 02429 vars[j] = tmp[i]; 02430 02431 if (tmp[i + 1] == '\0') 02432 break; 02433 if (tmp[i] == '\n') { 02434 vars[j++] = '\r'; 02435 vars[j++] = '\n'; 02436 02437 ast_copy_string(&(vars[j]), "Variable: ", len - j); 02438 j += 9; 02439 } 02440 } 02441 if (j > len - 3) 02442 j = len - 3; 02443 vars[j++] = '\r'; 02444 vars[j++] = '\n'; 02445 vars[j] = '\0'; 02446 } else { 02447 /* there are no channel variables; leave it blank */ 02448 *vars = '\0'; 02449 } 02450 return vars; 02451 }
| static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 4318 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
04319 { 04320 /* Don't need to hold the lock while we setup the outgoing calls */ 04321 int retrywait = qe->parent->retry * 1000; 04322 04323 int res = ast_waitfordigit(qe->chan, retrywait); 04324 if (res > 0 && !valid_exit(qe, res)) 04325 res = 0; 04326 04327 return res; 04328 }
| 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 2845 of file app_queue.c.
References ast_channel::_state, ast_channel::accountcode, 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(), ast_channel::call_forward, 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, ast_channel::name, 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().
02846 { 02847 const char *queue = qe->parent->name; 02848 struct callattempt *o, *start = NULL, *prev = NULL; 02849 int status; 02850 int numbusies = prebusies; 02851 int numnochan = 0; 02852 int stillgoing = 0; 02853 int orig = *to; 02854 struct ast_frame *f; 02855 struct callattempt *peer = NULL; 02856 struct ast_channel *winner; 02857 struct ast_channel *in = qe->chan; 02858 char on[80] = ""; 02859 char membername[80] = ""; 02860 long starttime = 0; 02861 long endtime = 0; 02862 #ifdef HAVE_EPOLL 02863 struct callattempt *epollo; 02864 #endif 02865 02866 starttime = (long) time(NULL); 02867 #ifdef HAVE_EPOLL 02868 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 02869 if (epollo->chan) 02870 ast_poll_channel_add(in, epollo->chan); 02871 } 02872 #endif 02873 02874 while (*to && !peer) { 02875 int numlines, retry, pos = 1; 02876 struct ast_channel *watchers[AST_MAX_WATCHERS]; 02877 watchers[0] = in; 02878 start = NULL; 02879 02880 for (retry = 0; retry < 2; retry++) { 02881 numlines = 0; 02882 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 02883 if (o->stillgoing) { /* Keep track of important channels */ 02884 stillgoing = 1; 02885 if (o->chan) { 02886 watchers[pos++] = o->chan; 02887 if (!start) 02888 start = o; 02889 else 02890 prev->call_next = o; 02891 prev = o; 02892 } 02893 } 02894 numlines++; 02895 } 02896 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 02897 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 02898 break; 02899 /* On "ringall" strategy we only move to the next penalty level 02900 when *all* ringing phones are done in the current penalty level */ 02901 ring_one(qe, outgoing, &numbusies); 02902 /* and retry... */ 02903 } 02904 if (pos == 1 /* not found */) { 02905 if (numlines == (numbusies + numnochan)) { 02906 ast_debug(1, "Everyone is busy at this time\n"); 02907 } else { 02908 ast_debug(3, "No one is answering queue '%s' (%d numlines / %d busies / %d failed channels)\n", queue, numlines, numbusies, numnochan); 02909 } 02910 *to = 0; 02911 return NULL; 02912 } 02913 02914 /* Poll for events from both the incoming channel as well as any outgoing channels */ 02915 winner = ast_waitfor_n(watchers, pos, to); 02916 02917 /* Service all of the outgoing channels */ 02918 for (o = start; o; o = o->call_next) { 02919 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 02920 if (!peer) { 02921 ast_verb(3, "%s answered %s\n", o->chan->name, in->name); 02922 peer = o; 02923 } 02924 } else if (o->chan && (o->chan == winner)) { 02925 02926 ast_copy_string(on, o->member->interface, sizeof(on)); 02927 ast_copy_string(membername, o->member->membername, sizeof(membername)); 02928 02929 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 02930 ast_verb(3, "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); 02931 numnochan++; 02932 do_hang(o); 02933 winner = NULL; 02934 continue; 02935 } else if (!ast_strlen_zero(o->chan->call_forward)) { 02936 char tmpchan[256]; 02937 char *stuff; 02938 char *tech; 02939 02940 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 02941 if ((stuff = strchr(tmpchan, '/'))) { 02942 *stuff++ = '\0'; 02943 tech = tmpchan; 02944 } else { 02945 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 02946 stuff = tmpchan; 02947 tech = "Local"; 02948 } 02949 /* Before processing channel, go ahead and check for forwarding */ 02950 ast_verb(3, "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); 02951 /* Setup parameters */ 02952 o->chan = ast_request(tech, in->nativeformats, stuff, &status); 02953 if (!o->chan) { 02954 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); 02955 o->stillgoing = 0; 02956 numnochan++; 02957 } else { 02958 ast_channel_inherit_variables(in, o->chan); 02959 ast_channel_datastore_inherit(in, o->chan); 02960 if (o->chan->cid.cid_num) 02961 ast_free(o->chan->cid.cid_num); 02962 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); 02963 02964 if (o->chan->cid.cid_name) 02965 ast_free(o->chan->cid.cid_name); 02966 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); 02967 02968 ast_string_field_set(o->chan, accountcode, in->accountcode); 02969 o->chan->cdrflags = in->cdrflags; 02970 02971 if (in->cid.cid_ani) { 02972 if (o->chan->cid.cid_ani) 02973 ast_free(o->chan->cid.cid_ani); 02974 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); 02975 } 02976 if (o->chan->cid.cid_rdnis) 02977 ast_free(o->chan->cid.cid_rdnis); 02978 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); 02979 if (ast_call(o->chan, tmpchan, 0)) { 02980 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); 02981 do_hang(o); 02982 numnochan++; 02983 } 02984 } 02985 /* Hangup the original channel now, in case we needed it */ 02986 ast_hangup(winner); 02987 continue; 02988 } 02989 f = ast_read(winner); 02990 if (f) { 02991 if (f->frametype == AST_FRAME_CONTROL) { 02992 switch (f->subclass) { 02993 case AST_CONTROL_ANSWER: 02994 /* This is our guy if someone answered. */ 02995 if (!peer) { 02996 ast_verb(3, "%s answered %s\n", o->chan->name, in->name); 02997 peer = o; 02998 } 02999 break; 03000 case AST_CONTROL_BUSY: 03001 ast_verb(3, "%s is busy\n", o->chan->name); 03002 if (in->cdr) 03003 ast_cdr_busy(in->cdr); 03004 do_hang(o); 03005 endtime = (long) time(NULL); 03006 endtime -= starttime; 03007 rna(endtime * 1000, qe, on, membername, 0); 03008 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03009 if (qe->parent->timeoutrestart) 03010 *to = orig; 03011 /* Have enough time for a queue member to answer? */ 03012 if (*to > 500) { 03013 ring_one(qe, outgoing, &numbusies); 03014 starttime = (long) time(NULL); 03015 } 03016 } 03017 numbusies++; 03018 break; 03019 case AST_CONTROL_CONGESTION: 03020 ast_verb(3, "%s is circuit-busy\n", o->chan->name); 03021 if (in->cdr) 03022 ast_cdr_busy(in->cdr); 03023 endtime = (long) time(NULL); 03024 endtime -= starttime; 03025 rna(endtime * 1000, qe, on, membername, 0); 03026 do_hang(o); 03027 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03028 if (qe->parent->timeoutrestart) 03029 *to = orig; 03030 if (*to > 500) { 03031 ring_one(qe, outgoing, &numbusies); 03032 starttime = (long) time(NULL); 03033 } 03034 } 03035 numbusies++; 03036 break; 03037 case AST_CONTROL_RINGING: 03038 ast_verb(3, "%s is ringing\n", o->chan->name); 03039 break; 03040 case AST_CONTROL_OFFHOOK: 03041 /* Ignore going off hook */ 03042 break; 03043 default: 03044 ast_debug(1, "Dunno what to do with control type %d\n", f->subclass); 03045 } 03046 } 03047 ast_frfree(f); 03048 } else { /* ast_read() returned NULL */ 03049 endtime = (long) time(NULL) - starttime; 03050 rna(endtime * 1000, qe, on, membername, 1); 03051 do_hang(o); 03052 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 03053 if (qe->parent->timeoutrestart) 03054 *to = orig; 03055 if (*to > 500) { 03056 ring_one(qe, outgoing, &numbusies); 03057 starttime = (long) time(NULL); 03058 } 03059 } 03060 } 03061 } 03062 } 03063 03064 /* If we received an event from the caller, deal with it. */ 03065 if (winner == in) { 03066 f = ast_read(in); 03067 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 03068 /* Got hung up */ 03069 *to = -1; 03070 if (f) { 03071 if (f->data.uint32) { 03072 in->hangupcause = f->data.uint32; 03073 } 03074 ast_frfree(f); 03075 } 03076 return NULL; 03077 } 03078 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { 03079 ast_verb(3, "User hit %c to disconnect call.\n", f->subclass); 03080 *to = 0; 03081 ast_frfree(f); 03082 return NULL; 03083 } 03084 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) { 03085 ast_verb(3, "User pressed digit: %c\n", f->subclass); 03086 *to = 0; 03087 *digit = f->subclass; 03088 ast_frfree(f); 03089 return NULL; 03090 } 03091 ast_frfree(f); 03092 } 03093 if (!*to) { 03094 for (o = start; o; o = o->call_next) 03095 rna(orig, qe, o->interface, o->member->membername, 1); 03096 } 03097 } 03098 03099 #ifdef HAVE_EPOLL 03100 for (epollo = outgoing; epollo; epollo = epollo->q_next) { 03101 if (epollo->chan) 03102 ast_poll_channel_del(in, epollo->chan); 03103 } 03104 #endif 03105 03106 return peer; 03107 }
| 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 3195 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_LEAVEEMPTY, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, status, penalty_rule::time, ast_channel::uniqueid, update_qe_rule(), and valid_exit().
Referenced by queue_exec().
03196 { 03197 int res = 0; 03198 03199 /* This is the holding pen for callers 2 through maxlen */ 03200 for (;;) { 03201 03202 if (is_our_turn(qe)) 03203 break; 03204 03205 /* If we have timed out, break out */ 03206 if (qe->expire && (time(NULL) >= qe->expire)) { 03207 *reason = QUEUE_TIMEOUT; 03208 break; 03209 } 03210 03211 if (qe->parent->leavewhenempty) { 03212 int status = 0; 03213 03214 if ((status = get_member_status(qe->parent, qe->max_penalty, qe->min_penalty, qe->parent->leavewhenempty))) { 03215 *reason = QUEUE_LEAVEEMPTY; 03216 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long) time(NULL) - qe->start); 03217 leave_queue(qe); 03218 break; 03219 } 03220 } 03221 03222 /* Make a position announcement, if enabled */ 03223 if (qe->parent->announcefrequency && 03224 (res = say_position(qe,ringing))) 03225 break; 03226 03227 /* If we have timed out, break out */ 03228 if (qe->expire && (time(NULL) >= qe->expire)) { 03229 *reason = QUEUE_TIMEOUT; 03230 break; 03231 } 03232 03233 /* Make a periodic announcement, if enabled */ 03234 if (qe->parent->periodicannouncefrequency && 03235 (res = say_periodic_announcement(qe,ringing))) 03236 break; 03237 03238 /* see if we need to move to the next penalty level for this queue */ 03239 while (qe->pr && ((time(NULL) - qe->start) >= qe->pr->time)) { 03240 update_qe_rule(qe); 03241 } 03242 03243 /* If we have timed out, break out */ 03244 if (qe->expire && (time(NULL) >= qe->expire)) { 03245 *reason = QUEUE_TIMEOUT; 03246 break; 03247 } 03248 03249 /* Wait a second before checking again */ 03250 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 03251 if (res > 0 && !valid_exit(qe, res)) 03252 res = 0; 03253 else 03254 break; 03255 } 03256 03257 /* If we have timed out, break out */ 03258 if (qe->expire && (time(NULL) >= qe->expire)) { 03259 *reason = QUEUE_TIMEOUT; 03260 break; 03261 } 03262 } 03263 03264 return res; 03265 }
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 = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 7292 of file app_queue.c.
char* app = "Queue" [static] |
Definition at line 540 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 542 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 546 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 550 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 544 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 548 of file app_queue.c.
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 7292 of file app_queue.c.
int autofill_default = 0 [static] |
queues.conf [general] option
Definition at line 564 of file app_queue.c.
struct ast_cli_entry cli_queue[] [static] |
Definition at line 7159 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_event_sub* device_state_sub [static] |
Subscription to device state change events.
Definition at line 573 of file app_queue.c.
struct ast_taskprocessor* devicestate_tps [static] |
Definition at line 524 of file app_queue.c.
| enum queue_result id |
Definition at line 590 of file app_queue.c.
Referenced by _sip_show_peers(), _skinny_show_devices(), _skinny_show_lines(), amixer_max(), idemodulator(), and setamixer().
int montype_default = 0 [static] |
queues.conf [general] option
Definition at line 567 of file app_queue.c.
const char* pm_family = "Queue/PersistentMembers" [static] |
Persistent Members astdb family.
Definition at line 553 of file app_queue.c.
const char qpm_cmd_usage[] [static] |
"Usage: queue pause member <channel> in <queue> reason <reason>\n"
Definition at line 7150 of file app_queue.c.
const char qsmp_cmd_usage[] [static] |
"Usage: queue set member penalty <channel> from <queue> <penalty>\n"
Definition at line 7156 of file app_queue.c.
int queue_persistent_members = 0 [static] |
queues.conf [general] option
Definition at line 558 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 3434 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] |
{
.name = "QUEUE_MEMBER_COUNT",
.read = queue_function_qac_dep,
}
Definition at line 5623 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuemembercount_function [static] |
{
.name = "QUEUE_MEMBER",
.read = queue_function_qac,
}
Definition at line 5618 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberlist_function [static] |
{
.name = "QUEUE_MEMBER_LIST",
.read = queue_function_queuememberlist,
}
Definition at line 5633 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberpenalty_function [static] |
{
.name = "QUEUE_MEMBER_PENALTY",
.read = queue_function_memberpenalty_read,
.write = queue_function_memberpenalty_write,
}
Definition at line 5638 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ao2_container* queues [static] |
Definition at line 820 of file app_queue.c.
Referenced by __queues_show(), add_to_queue(), clear_stats(), compare_weight(), complete_queue(), complete_queue_remove_member(), find_queue_by_name_rt(), get_member_penalty(), handle_statechange(), 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(), reload_single_queue(), remove_from_queue(), set_member_paused(), set_member_penalty(), try_calling(), unload_module(), update_queue(), and update_realtime_members().
struct ast_custom_function queuevar_function [static] |
{
.name = "QUEUE_VARIABLES",
.read = queue_function_var,
}
Definition at line 5613 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuewaitingcount_function [static] |
{
.name = "QUEUE_WAITING_COUNT",
.read = queue_function_queuewaitingcount,
}
Definition at line 5628 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 7153 of file app_queue.c.
int shared_lastcall = 0 [static] |
queues.conf [general] option
Definition at line 570 of file app_queue.c.
struct strategy strategies[] [static] |
Referenced by int2strat(), and strat2int().
| char* text |
Definition at line 591 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 576 of file app_queue.c.
Referenced by login_exec().
int use_weight = 0 [static] |
queues.conf per-queue weight option
Definition at line 561 of file app_queue.c.
1.6.2