True call queues with optional send URL on answer. More...
#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.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/astobj2.h"
#include "asterisk/global_datastores.h"
Go to the source code of this file.
Data Structures | |
struct | call_queue |
struct | callattempt |
We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More... | |
struct | interfaces |
struct | member |
struct | member_count |
struct | member_interface |
struct | queue_ent |
struct | queue_transfer_ds |
struct | queues |
struct | statechange |
struct | strategy |
Defines | |
#define | ANNOUNCEHOLDTIME_ALWAYS 1 |
#define | ANNOUNCEHOLDTIME_ONCE 2 |
#define | AST_MAX_WATCHERS 256 |
#define | DEFAULT_RETRY 5 |
#define | DEFAULT_TIMEOUT 15 |
#define | MAX_PERIODIC_ANNOUNCEMENTS 10 |
#define | PM_MAX_LEN 8192 |
#define | QUEUE_EMPTY_NORMAL 1 |
#define | QUEUE_EMPTY_STRICT 2 |
#define | QUEUE_EVENT_VARIABLES 3 |
#define | RECHECK 1 |
#define | RES_EXISTS (-1) |
#define | RES_NOSUCHQUEUE (-3) |
#define | RES_NOT_DYNAMIC (-4) |
#define | RES_OKAY 0 |
#define | RES_OUTOFMEMORY (-2) |
Enumerations | |
enum | { QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED } |
enum | qmc_status { QMC_VALID = 0, QMC_PAUSED, QMC_ACTIVE, QMC_FREE, QMC_ALL } |
enum | queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL } |
enum | queue_result { QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3, QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6 } |
Functions | |
static int | __queues_show (struct mansession *s, int manager, int fd, int argc, char **argv) |
static void | __reg_module (void) |
static void | __unreg_module (void) |
static int | add_to_interfaces (const char *interface) |
static int | add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface) |
static struct call_queue * | alloc_queue (const char *queuename) |
static int | aqm_exec (struct ast_channel *chan, void *data) |
static int | attended_transfer_occurred (struct ast_channel *chan) |
mechanism to tell if a queue caller was atxferred by a queue member. | |
static int | calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp) |
Calculate the metric of each member in the outgoing callattempts. | |
static void | clear_and_free_interfaces (void) |
static void | clear_queue (struct call_queue *q) |
static int | cli_queue_member_count (int fd, int argc, char **argv) |
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_member_count (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_show (const char *line, const char *word, int pos, int state) |
static int | compress_char (const char c) |
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) |
static void * | device_state_thread (void *data) |
Consumer of the statechange queue. | |
static void | do_hang (struct callattempt *o) |
common hangup actions | |
static void | dump_queue_members (struct call_queue *pm_queue) |
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) |
static enum queue_member_status | get_member_status (struct call_queue *q, int max_penalty) |
Check if members are available. | |
static int | handle_queue_add_member (int fd, int argc, char *argv[]) |
static int | handle_queue_remove_member (int fd, int argc, char *argv[]) |
static void * | handle_statechange (struct statechange *sc) |
set a member's status based on device state of that member's interface | |
static void | hangupcalls (struct callattempt *outgoing, struct ast_channel *exception) |
static void | init_queue (struct call_queue *q) |
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 char * | int2strat (int strategy) |
static struct member * | interface_exists (struct call_queue *q, const char *interface) |
static int | interface_exists_global (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 void | leave_queue (struct queue_ent *qe) |
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_member_count (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) |
static int | manager_remove_queue_member (struct mansession *s, const struct message *m) |
static int | member_cmp_fn (void *obj1, void *obj2, int flags) |
static int | member_hash_fn (const void *obj, const int flags) |
static void | monjoin_dep_warning (void) |
static int | num_available_members (struct call_queue *q) |
Get the number of members available to accept a call. | |
static int | play_file (struct ast_channel *chan, char *filename) |
static int | pqm_exec (struct ast_channel *chan, void *data) |
static int | ql_exec (struct ast_channel *chan, void *data) |
static int | qmc_handler (const char *queuename, char *buffer, int len) |
static int | queue_exec (struct ast_channel *chan, void *data) |
The starting point for all queue calls. | |
static int | queue_function_exists (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
static int | queue_function_queuemembercount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
static int | queue_function_queuememberlist (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
static int | queue_function_queuememberpaused (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
static int | queue_function_queuememberstatus (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
static int | queue_function_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
static int | queue_member_count (const char *qname, struct member_count *qmc) |
static void | queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown) |
Configure a queue parameter. | |
static int | queue_show (int fd, int argc, char **argv) |
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 void | recalc_holdtime (struct queue_ent *qe, int newholdtime) |
static void | record_abandoned (struct queue_ent *qe) |
static int | reload (void) |
static void | reload_queue_members (void) |
static int | reload_queues (void) |
static int | remove_from_interfaces (const char *interface) |
static int | remove_from_queue (const char *queuename, const char *interface) |
static void | remove_queue (struct call_queue *q) |
removes a call_queue from the list of call_queues | |
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) |
static void | rr_dep_warning (void) |
static void | rt_handle_member_record (struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface) |
static int | say_periodic_announcement (struct queue_ent *qe) |
static int | say_position (struct queue_ent *qe) |
static int | set_member_paused (const char *queuename, const char *interface, int paused) |
static void | set_queue_result (struct ast_channel *chan, enum queue_result res) |
sets the QUEUESTATUS channel variable | |
static struct ast_datastore * | setup_transfer_datastore (struct queue_ent *qe, struct member *member, time_t starttime, int callcompletedinsl) |
create a datastore for storing relevant info to log attended transfers in the queue_log | |
static int | statechange_queue (const char *dev, int state, void *ign) |
Producer of the statechange queue. | |
static int | store_next (struct queue_ent *qe, struct callattempt *outgoing) |
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) |
A large function which calls members, updates statistics, and bridges the caller and a member. | |
static int | unload_module (void) |
static int | update_queue (struct call_queue *q, struct member *member, int callcompletedinsl) |
static int | update_realtime_member_field (struct member *mem, const char *queue_name, const char *field, const char *value) |
static void | update_realtime_members (struct call_queue *q) |
static int | update_status (const char *interface, const int status) |
static int | upqm_exec (struct ast_channel *chan, void *data) |
static int | valid_exit (struct queue_ent *qe, char digit) |
static char * | vars2manager (struct ast_channel *chan, char *vars, size_t len) |
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 | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } |
static char * | app = "Queue" |
static char * | app_aqm = "AddQueueMember" |
static char * | app_aqm_descrip |
static char * | app_aqm_synopsis = "Dynamically adds queue members" |
static char * | app_pqm = "PauseQueueMember" |
static char * | app_pqm_descrip |
static char * | app_pqm_synopsis = "Pauses a queue member" |
static char * | app_ql = "QueueLog" |
static char * | app_ql_descrip |
static char * | app_ql_synopsis = "Writes to the queue_log" |
static char * | app_rqm = "RemoveQueueMember" |
static char * | app_rqm_descrip |
static char * | app_rqm_synopsis = "Dynamically removes queue members" |
static char * | app_upqm = "UnpauseQueueMember" |
static char * | app_upqm_descrip |
static char * | app_upqm_synopsis = "Unpauses a queue member" |
static const struct ast_module_info * | ast_module_info = &__mod_info |
static int | autofill_default = 0 |
queues.conf [general] option | |
static struct ast_cli_entry | cli_add_queue_member_deprecated |
static struct ast_cli_entry | cli_queue [] |
static struct ast_cli_entry | cli_remove_queue_member_deprecated |
static struct ast_cli_entry | cli_show_queue_deprecated |
static char * | descrip |
struct { | |
ast_cond_t cond | |
ast_mutex_t lock | |
struct { | |
struct statechange * first | |
struct statechange * last | |
} state_change_q | |
unsigned int stop:1 | |
pthread_t thread | |
} | device_state |
Data used by the device state thread. | |
static int | montype_default = 0 |
queues.conf [general] option | |
static const char * | pm_family = "Queue/PersistentMembers" |
Persistent Members astdb family. | |
static char | qam_cmd_usage [] |
static char | qmc_cmd_usage [] |
static char | qrm_cmd_usage [] |
static int | queue_debug = 0 |
queues.conf [general] extra debug option | |
static int | queue_persistent_members = 0 |
queues.conf [general] option | |
struct { | |
enum queue_result id | |
char * text | |
} | queue_results [] |
static char | queue_show_usage [] |
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 | queueagentcount_function |
static struct ast_custom_function | queueexists_function |
static struct ast_custom_function | queuemembercount_function |
static struct ast_custom_function | queuememberlist_function |
static struct ast_custom_function | queuememberpaused_function |
static struct ast_custom_function | queuememberstatus_function |
static struct ast_custom_function | queuewaitingcount_function |
static int | shared_lastcall = 0 |
queues.conf [general] option | |
static struct strategy | strategies [] |
static char * | synopsis = "Queue a call for a call queue" |
static int | 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 399 of file app_queue.c.
Referenced by queue_set_param().
#define ANNOUNCEHOLDTIME_ONCE 2 |
Definition at line 400 of file app_queue.c.
Referenced by queue_set_param(), and say_position().
#define AST_MAX_WATCHERS 256 |
Definition at line 2287 of file app_queue.c.
#define DEFAULT_RETRY 5 |
Definition at line 139 of file app_queue.c.
Referenced by init_queue(), and queue_set_param().
#define DEFAULT_TIMEOUT 15 |
Definition at line 140 of file app_queue.c.
Referenced by queue_set_param().
#define MAX_PERIODIC_ANNOUNCEMENTS 10 |
Definition at line 142 of file app_queue.c.
Referenced by init_queue(), queue_set_param(), and say_periodic_announcement().
#define PM_MAX_LEN 8192 |
Definition at line 279 of file app_queue.c.
Referenced by dump_queue_members(), and reload_queue_members().
#define QUEUE_EMPTY_NORMAL 1 |
Definition at line 397 of file app_queue.c.
Referenced by queue_set_param().
#define QUEUE_EMPTY_STRICT 2 |
Definition at line 398 of file app_queue.c.
Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().
#define QUEUE_EVENT_VARIABLES 3 |
Definition at line 401 of file app_queue.c.
Referenced by queue_set_param(), ring_entry(), and try_calling().
#define RECHECK 1 |
Definition at line 141 of file app_queue.c.
Referenced by wait_our_turn().
#define RES_EXISTS (-1) |
Definition at line 145 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_NOSUCHQUEUE (-3) |
Definition at line 147 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_NOT_DYNAMIC (-4) |
Definition at line 148 of file app_queue.c.
Referenced by handle_queue_remove_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_OKAY 0 |
Definition at line 144 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().
#define RES_OUTOFMEMORY (-2) |
Definition at line 146 of file app_queue.c.
Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), and reload_queue_members().
anonymous enum |
QUEUE_STRATEGY_RINGALL | |
QUEUE_STRATEGY_ROUNDROBIN | |
QUEUE_STRATEGY_LEASTRECENT | |
QUEUE_STRATEGY_FEWESTCALLS | |
QUEUE_STRATEGY_RANDOM | |
QUEUE_STRATEGY_RRMEMORY | |
QUEUE_STRATEGY_RRORDERED |
Definition at line 116 of file app_queue.c.
00116 { 00117 QUEUE_STRATEGY_RINGALL = 0, 00118 QUEUE_STRATEGY_ROUNDROBIN, 00119 QUEUE_STRATEGY_LEASTRECENT, 00120 QUEUE_STRATEGY_FEWESTCALLS, 00121 QUEUE_STRATEGY_RANDOM, 00122 QUEUE_STRATEGY_RRMEMORY, 00123 QUEUE_STRATEGY_RRORDERED, 00124 };
enum qmc_status |
Definition at line 4448 of file app_queue.c.
04448 { 04449 QMC_VALID = 0, /* Count valid members */ 04450 QMC_PAUSED, /* Count paused members */ 04451 QMC_ACTIVE, /* Count active members */ 04452 QMC_FREE, /* Count free members */ 04453 QMC_ALL /* Count all queue members */ 04454 };
enum queue_member_status |
Definition at line 577 of file app_queue.c.
00577 { 00578 QUEUE_NO_MEMBERS, 00579 QUEUE_NO_REACHABLE_MEMBERS, 00580 QUEUE_NORMAL 00581 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL |
Definition at line 299 of file app_queue.c.
00299 { 00300 QUEUE_UNKNOWN = 0, 00301 QUEUE_TIMEOUT = 1, 00302 QUEUE_JOINEMPTY = 2, 00303 QUEUE_LEAVEEMPTY = 3, 00304 QUEUE_JOINUNAVAIL = 4, 00305 QUEUE_LEAVEUNAVAIL = 5, 00306 QUEUE_FULL = 6, 00307 };
static int __queues_show | ( | struct mansession * | s, | |
int | manager, | |||
int | fd, | |||
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 5060 of file app_queue.c.
References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_build_string(), ast_category_browse(), ast_check_realtime(), ast_cli(), ast_config_destroy(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime_multientry(), ast_strlen_zero(), astman_append(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, devstate2str(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::prio, queue_show(), member::realtime, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::ringlimit, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
05061 { 05062 struct call_queue *q; 05063 struct queue_ent *qe; 05064 struct member *mem; 05065 int pos, queue_show; 05066 time_t now; 05067 char max_buf[150]; 05068 char *max; 05069 size_t max_left; 05070 float sl = 0; 05071 char *term = manager ? "\r\n" : "\n"; 05072 struct ao2_iterator mem_iter; 05073 05074 time(&now); 05075 if (argc == 2) 05076 queue_show = 0; 05077 else if (argc == 3) 05078 queue_show = 1; 05079 else 05080 return RESULT_SHOWUSAGE; 05081 05082 /* We only want to load realtime queues when a specific queue is asked for. */ 05083 if (queue_show) { 05084 load_realtime_queue(argv[2]); 05085 } else if (ast_check_realtime("queues")) { 05086 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL); 05087 char *queuename; 05088 if (cfg) { 05089 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 05090 load_realtime_queue(queuename); 05091 } 05092 ast_config_destroy(cfg); 05093 } 05094 } 05095 05096 AST_LIST_LOCK(&queues); 05097 if (AST_LIST_EMPTY(&queues)) { 05098 AST_LIST_UNLOCK(&queues); 05099 if (queue_show) { 05100 if (s) 05101 astman_append(s, "No such queue: %s.%s",argv[2], term); 05102 else 05103 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 05104 } else { 05105 if (s) 05106 astman_append(s, "No queues.%s", term); 05107 else 05108 ast_cli(fd, "No queues.%s", term); 05109 } 05110 return RESULT_SUCCESS; 05111 } 05112 AST_LIST_TRAVERSE(&queues, q, list) { 05113 ao2_lock(q); 05114 if (queue_show) { 05115 if (strcasecmp(q->name, argv[2]) != 0) { 05116 ao2_unlock(q); 05117 if (!AST_LIST_NEXT(q, list)) { 05118 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 05119 break; 05120 } 05121 continue; 05122 } 05123 } 05124 max_buf[0] = '\0'; 05125 max = max_buf; 05126 max_left = sizeof(max_buf); 05127 if (q->maxlen) 05128 ast_build_string(&max, &max_left, "%d", q->maxlen); 05129 else 05130 ast_build_string(&max, &max_left, "unlimited"); 05131 sl = 0; 05132 if (q->callscompleted > 0) 05133 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 05134 if (s) 05135 astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", 05136 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit, 05137 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term); 05138 else 05139 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", 05140 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit, 05141 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term); 05142 if (ao2_container_count(q->members)) { 05143 if (s) 05144 astman_append(s, " Members: %s", term); 05145 else 05146 ast_cli(fd, " Members: %s", term); 05147 mem_iter = ao2_iterator_init(q->members, 0); 05148 while ((mem = ao2_iterator_next(&mem_iter))) { 05149 max_buf[0] = '\0'; 05150 max = max_buf; 05151 max_left = sizeof(max_buf); 05152 if (strcasecmp(mem->membername, mem->interface)) { 05153 ast_build_string(&max, &max_left, " (%s)", mem->interface); 05154 } 05155 if (mem->penalty) 05156 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty); 05157 if (mem->dynamic) 05158 ast_build_string(&max, &max_left, " (dynamic)"); 05159 if (mem->realtime) 05160 ast_build_string(&max, &max_left, " (realtime)"); 05161 if (mem->paused) 05162 ast_build_string(&max, &max_left, " (paused)"); 05163 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status)); 05164 if (mem->calls) { 05165 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)", 05166 mem->calls, (long) (time(NULL) - mem->lastcall)); 05167 } else 05168 ast_build_string(&max, &max_left, " has taken no calls yet"); 05169 if (s) 05170 astman_append(s, " %s%s%s", mem->membername, max_buf, term); 05171 else 05172 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term); 05173 ao2_ref(mem, -1); 05174 } 05175 ao2_iterator_destroy(&mem_iter); 05176 } else if (s) 05177 astman_append(s, " No Members%s", term); 05178 else 05179 ast_cli(fd, " No Members%s", term); 05180 if (q->head) { 05181 pos = 1; 05182 if (s) 05183 astman_append(s, " Callers: %s", term); 05184 else 05185 ast_cli(fd, " Callers: %s", term); 05186 for (qe = q->head; qe; qe = qe->next) { 05187 if (s) 05188 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", 05189 pos++, qe->chan->name, (long) (now - qe->start) / 60, 05190 (long) (now - qe->start) % 60, qe->prio, term); 05191 else 05192 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, 05193 qe->chan->name, (long) (now - qe->start) / 60, 05194 (long) (now - qe->start) % 60, qe->prio, term); 05195 } 05196 } else if (s) 05197 astman_append(s, " No Callers%s", term); 05198 else 05199 ast_cli(fd, " No Callers%s", term); 05200 if (s) 05201 astman_append(s, "%s", term); 05202 else 05203 ast_cli(fd, "%s", term); 05204 ao2_unlock(q); 05205 if (queue_show) 05206 break; 05207 } 05208 AST_LIST_UNLOCK(&queues); 05209 return RESULT_SUCCESS; 05210 }
static void __reg_module | ( | void | ) | [static] |
Definition at line 5894 of file app_queue.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 5894 of file app_queue.c.
static int add_to_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 938 of file app_queue.c.
References ast_calloc, ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), member_interface::interface, LOG_DEBUG, and option_debug.
Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().
00939 { 00940 struct member_interface *curint; 00941 00942 AST_LIST_LOCK(&interfaces); 00943 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00944 if (!strcasecmp(curint->interface, interface)) 00945 break; 00946 } 00947 00948 if (curint) { 00949 AST_LIST_UNLOCK(&interfaces); 00950 return 0; 00951 } 00952 00953 if (option_debug) 00954 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface); 00955 00956 if ((curint = ast_calloc(1, sizeof(*curint)))) { 00957 ast_copy_string(curint->interface, interface, sizeof(curint->interface)); 00958 AST_LIST_INSERT_HEAD(&interfaces, curint, list); 00959 } 00960 AST_LIST_UNLOCK(&interfaces); 00961 00962 return 0; 00963 }
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] |
Definition at line 3671 of file app_queue.c.
References add_to_interfaces(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_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, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, member::state_interface, and member::status.
Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().
03672 { 03673 struct call_queue *q; 03674 struct member *new_member, *old_member; 03675 int res = RES_NOSUCHQUEUE; 03676 03677 /* \note Ensure the appropriate realtime queue is loaded. Note that this 03678 * short-circuits if the queue is already in memory. */ 03679 if (!(q = load_realtime_queue(queuename))) 03680 return res; 03681 03682 AST_LIST_LOCK(&queues); 03683 03684 ao2_lock(q); 03685 if ((old_member = interface_exists(q, interface)) == NULL) { 03686 if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) { 03687 add_to_interfaces(new_member->state_interface); 03688 new_member->dynamic = 1; 03689 ao2_link(q->members, new_member); 03690 q->membercount++; 03691 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 03692 "Queue: %s\r\n" 03693 "Location: %s\r\n" 03694 "MemberName: %s\r\n" 03695 "Membership: %s\r\n" 03696 "Penalty: %d\r\n" 03697 "CallsTaken: %d\r\n" 03698 "LastCall: %d\r\n" 03699 "Status: %d\r\n" 03700 "Paused: %d\r\n", 03701 q->name, new_member->interface, new_member->membername, 03702 "dynamic", 03703 new_member->penalty, new_member->calls, (int) new_member->lastcall, 03704 new_member->status, new_member->paused); 03705 03706 ao2_ref(new_member, -1); 03707 new_member = NULL; 03708 03709 if (dump) 03710 dump_queue_members(q); 03711 03712 res = RES_OKAY; 03713 } else { 03714 res = RES_OUTOFMEMORY; 03715 } 03716 } else { 03717 ao2_ref(old_member, -1); 03718 res = RES_EXISTS; 03719 } 03720 ao2_unlock(q); 03721 AST_LIST_UNLOCK(&queues); 03722 03723 return res; 03724 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static, read] |
Definition at line 835 of file app_queue.c.
References ao2_alloc(), ast_copy_string(), destroy_queue(), and call_queue::name.
Referenced by find_queue_by_name_rt(), and reload_queues().
00836 { 00837 struct call_queue *q; 00838 00839 if ((q = ao2_alloc(sizeof(*q), destroy_queue))) { 00840 ast_copy_string(q->name, queuename, sizeof(q->name)); 00841 } 00842 return q; 00843 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 4052 of file app_queue.c.
References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.
Referenced by load_module().
04053 { 04054 int res=-1; 04055 struct ast_module_user *lu; 04056 char *parse, *temppos = NULL; 04057 int priority_jump = 0; 04058 AST_DECLARE_APP_ARGS(args, 04059 AST_APP_ARG(queuename); 04060 AST_APP_ARG(interface); 04061 AST_APP_ARG(penalty); 04062 AST_APP_ARG(options); 04063 AST_APP_ARG(membername); 04064 AST_APP_ARG(state_interface); 04065 ); 04066 int penalty = 0; 04067 04068 if (ast_strlen_zero(data)) { 04069 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n"); 04070 return -1; 04071 } 04072 04073 parse = ast_strdupa(data); 04074 04075 AST_STANDARD_APP_ARGS(args, parse); 04076 04077 lu = ast_module_user_add(chan); 04078 04079 if (ast_strlen_zero(args.interface)) { 04080 args.interface = ast_strdupa(chan->name); 04081 temppos = strrchr(args.interface, '-'); 04082 if (temppos) 04083 *temppos = '\0'; 04084 } 04085 04086 if (!ast_strlen_zero(args.penalty)) { 04087 if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) { 04088 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 04089 penalty = 0; 04090 } 04091 } 04092 04093 if (args.options) { 04094 if (strchr(args.options, 'j')) 04095 priority_jump = 1; 04096 } 04097 04098 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) { 04099 case RES_OKAY: 04100 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 04101 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 04102 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 04103 res = 0; 04104 break; 04105 case RES_EXISTS: 04106 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 04107 if (priority_jump || ast_opt_priority_jumping) 04108 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 04109 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 04110 res = 0; 04111 break; 04112 case RES_NOSUCHQUEUE: 04113 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 04114 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 04115 res = 0; 04116 break; 04117 case RES_OUTOFMEMORY: 04118 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 04119 break; 04120 } 04121 04122 ast_module_user_remove(lu); 04123 04124 return res; 04125 }
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 2878 of file app_queue.c.
References ast_channel_datastore_find(), and queue_transfer_info.
Referenced by try_calling().
02879 { 02880 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 02881 }
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
Definition at line 2755 of file app_queue.c.
References ast_log(), ast_random(), member::calls, member::interface, member::lastcall, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, option_debug, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, member::ringcount, call_queue::ringlimit, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
Referenced by try_calling().
02756 { 02757 if (qe->max_penalty && (mem->penalty > qe->max_penalty)) 02758 return -1; 02759 02760 switch (q->strategy) { 02761 case QUEUE_STRATEGY_RINGALL: 02762 /* Everyone equal, except for penalty */ 02763 tmp->metric = mem->penalty * 1000000; 02764 break; 02765 case QUEUE_STRATEGY_ROUNDROBIN: 02766 if (!pos) { 02767 if (!q->wrapped) { 02768 /* No more channels, start over */ 02769 q->rrpos = 0; 02770 } else { 02771 /* Prioritize next entry */ 02772 q->rrpos++; 02773 } 02774 q->wrapped = 0; 02775 } 02776 /* Fall through */ 02777 case QUEUE_STRATEGY_RRORDERED: 02778 case QUEUE_STRATEGY_RRMEMORY: 02779 if (pos < q->rrpos) { 02780 tmp->metric = 1000 + pos; 02781 } else { 02782 if (pos > q->rrpos) 02783 /* Indicate there is another priority */ 02784 q->wrapped = 1; 02785 tmp->metric = pos; 02786 } 02787 tmp->metric += mem->penalty * 1000000; 02788 break; 02789 case QUEUE_STRATEGY_RANDOM: 02790 tmp->metric = ast_random() % 1000; 02791 tmp->metric += mem->penalty * 1000000; 02792 break; 02793 case QUEUE_STRATEGY_FEWESTCALLS: 02794 tmp->metric = mem->calls; 02795 tmp->metric += mem->penalty * 1000000; 02796 break; 02797 case QUEUE_STRATEGY_LEASTRECENT: 02798 if (!mem->lastcall) 02799 tmp->metric = 0; 02800 else 02801 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 02802 tmp->metric += mem->penalty * 1000000; 02803 break; 02804 default: 02805 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 02806 break; 02807 } 02808 if (q->ringlimit && (mem->ringcount >= q->ringlimit)) { 02809 tmp->metric += (mem->ringcount / q->ringlimit) * 10000000; 02810 } 02811 if (option_debug) 02812 ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 02813 tmp->metric, mem->interface, mem->ringcount, q->ringlimit); 02814 return 0; 02815 }
static void clear_and_free_interfaces | ( | void | ) | [static] |
Definition at line 1017 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.
Referenced by unload_module().
01018 { 01019 struct member_interface *curint; 01020 01021 AST_LIST_LOCK(&interfaces); 01022 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list))) 01023 free(curint); 01024 AST_LIST_UNLOCK(&interfaces); 01025 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 929 of file app_queue.c.
References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_queues().
00930 { 00931 q->holdtime = 0; 00932 q->callscompleted = 0; 00933 q->callsabandoned = 0; 00934 q->callscompletedinsl = 0; 00935 q->wrapuptime = 0; 00936 }
static int cli_queue_member_count | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 5715 of file app_queue.c.
References ast_cli(), qmc_handler(), RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.
05716 { 05717 char buffer[256] = ""; 05718 char *queuename; 05719 05720 if (argc != 4) { 05721 return RESULT_SHOWUSAGE; 05722 } 05723 queuename = argv[3]; 05724 05725 if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) { 05726 ast_cli(fd, 05727 "Member count for queue '%s'\n" 05728 "%s\n", 05729 queuename, buffer); 05730 return RESULT_SUCCESS; 05731 } else { 05732 ast_cli(fd, "No such queue: '%s'\n", queuename); 05733 return RESULT_FAILURE; 05734 } 05735 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 1876 of file app_queue.c.
References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_TRAVERSE, ast_log(), call_queue::count, member::interface, LOG_DEBUG, call_queue::members, call_queue::name, num_available_members(), and call_queue::weight.
Referenced by ring_entry().
01877 { 01878 struct call_queue *q; 01879 struct member *mem; 01880 int found = 0; 01881 01882 /* &qlock and &rq->lock already set by try_calling() 01883 * to solve deadlock */ 01884 AST_LIST_TRAVERSE(&queues, q, list) { 01885 if (q == rq) /* don't check myself, could deadlock */ 01886 continue; 01887 ao2_lock(q); 01888 if (q->count && q->members) { 01889 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 01890 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 01891 if (q->weight > rq->weight && q->count >= num_available_members(q)) { 01892 ast_log(LOG_DEBUG, "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); 01893 found = 1; 01894 } 01895 ao2_ref(mem, -1); 01896 } 01897 } 01898 ao2_unlock(q); 01899 if (found) 01900 break; 01901 } 01902 return found; 01903 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5217 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, and call_queue::name.
Referenced by complete_queue_add_member(), complete_queue_member_count(), complete_queue_remove_member(), and complete_queue_show().
05218 { 05219 struct call_queue *q; 05220 char *ret = NULL; 05221 int which = 0; 05222 int wordlen = strlen(word); 05223 05224 AST_LIST_LOCK(&queues); 05225 AST_LIST_TRAVERSE(&queues, q, list) { 05226 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 05227 ret = ast_strdup(q->name); 05228 break; 05229 } 05230 } 05231 AST_LIST_UNLOCK(&queues); 05232 05233 return ret; 05234 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5520 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
05521 { 05522 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 05523 switch (pos) { 05524 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 05525 return NULL; 05526 case 4: /* only one possible match, "to" */ 05527 return state == 0 ? ast_strdup("to") : NULL; 05528 case 5: /* <queue> */ 05529 return complete_queue(line, word, pos, state); 05530 case 6: /* only one possible match, "penalty" */ 05531 return state == 0 ? ast_strdup("penalty") : NULL; 05532 case 7: 05533 if (state < 100) { /* 0-99 */ 05534 char *num; 05535 if ((num = ast_malloc(3))) { 05536 sprintf(num, "%d", state); 05537 } 05538 return num; 05539 } else { 05540 return NULL; 05541 } 05542 case 8: /* only one possible match, "as" */ 05543 return state == 0 ? ast_strdup("as") : NULL; 05544 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 05545 return NULL; 05546 case 10: 05547 return state == 0 ? ast_strdup("state_interface") : NULL; 05548 default: 05549 return NULL; 05550 } 05551 }
static char* complete_queue_member_count | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5742 of file app_queue.c.
References complete_queue().
05743 { 05744 /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */ 05745 switch (pos) { 05746 case 3: /* <queue> */ 05747 return complete_queue(line, word, pos, state); 05748 default: 05749 return NULL; 05750 } 05751 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5588 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_strdup, complete_queue(), member::interface, and call_queue::members.
05589 { 05590 int which = 0; 05591 struct call_queue *q; 05592 struct member *m; 05593 struct ao2_iterator mem_iter; 05594 05595 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 05596 if (pos > 5 || pos < 3) 05597 return NULL; 05598 if (pos == 4) /* only one possible match, 'from' */ 05599 return state == 0 ? ast_strdup("from") : NULL; 05600 05601 if (pos == 5) /* No need to duplicate code */ 05602 return complete_queue(line, word, pos, state); 05603 05604 /* here is the case for 3, <member> */ 05605 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ 05606 AST_LIST_TRAVERSE(&queues, q, list) { 05607 ao2_lock(q); 05608 mem_iter = ao2_iterator_init(q->members, 0); 05609 while ((m = ao2_iterator_next(&mem_iter))) { 05610 if (++which > state) { 05611 char *tmp; 05612 ao2_iterator_destroy(&mem_iter); 05613 ao2_unlock(q); 05614 tmp = ast_strdup(m->interface); 05615 ao2_ref(m, -1); 05616 return tmp; 05617 } 05618 ao2_ref(m, -1); 05619 } 05620 ao2_iterator_destroy(&mem_iter); 05621 ao2_unlock(q); 05622 } 05623 } 05624 05625 return NULL; 05626 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5236 of file app_queue.c.
References complete_queue().
05237 { 05238 if (pos == 2) 05239 return complete_queue(line, word, pos, state); 05240 return NULL; 05241 }
static int compress_char | ( | const char | c | ) | [static] |
Definition at line 845 of file app_queue.c.
Referenced by member_hash_fn().
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 810 of file app_queue.c.
References ao2_alloc(), ast_copy_string(), ast_log(), ast_strlen_zero(), member::interface, LOG_WARNING, member::membername, member::paused, member::penalty, member::state_interface, and member::status.
Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().
00811 { 00812 struct member *cur; 00813 00814 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 00815 cur->penalty = penalty; 00816 cur->paused = paused; 00817 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 00818 if (!ast_strlen_zero(state_interface)) { 00819 ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface)); 00820 } else { 00821 ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface)); 00822 } 00823 if (!ast_strlen_zero(membername)) 00824 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 00825 else 00826 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 00827 if (!strchr(cur->interface, '/')) 00828 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 00829 cur->status = ast_device_state(cur->state_interface); 00830 } 00831 00832 return cur; 00833 }
static void destroy_queue | ( | void * | obj | ) | [static] |
Definition at line 543 of file app_queue.c.
References ao2_ref(), free_members(), and call_queue::members.
Referenced by alloc_queue().
00544 { 00545 struct call_queue *q = obj; 00546 if (q->members) { 00547 free_members(q, 1); 00548 ao2_ref(q->members, -1); 00549 } 00550 }
static void* device_state_thread | ( | void * | data | ) | [static] |
Consumer of the statechange queue.
Definition at line 758 of file app_queue.c.
References ast_cond_wait(), AST_LIST_REMOVE_HEAD, ast_mutex_lock(), ast_mutex_unlock(), device_state, statechange::entry, free, and handle_statechange().
Referenced by load_module().
00759 { 00760 struct statechange *sc = NULL; 00761 00762 while (!device_state.stop) { 00763 ast_mutex_lock(&device_state.lock); 00764 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) { 00765 ast_cond_wait(&device_state.cond, &device_state.lock); 00766 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry); 00767 } 00768 ast_mutex_unlock(&device_state.lock); 00769 00770 /* Check to see if we were woken up to see the request to stop */ 00771 if (device_state.stop) 00772 break; 00773 00774 if (!sc) 00775 continue; 00776 00777 handle_statechange(sc); 00778 00779 free(sc); 00780 sc = NULL; 00781 } 00782 00783 if (sc) 00784 free(sc); 00785 00786 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) 00787 free(sc); 00788 00789 return NULL; 00790 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 1906 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry(), and wait_for_answer().
01907 { 01908 o->stillgoing = 0; 01909 ast_hangup(o->chan); 01910 o->chan = NULL; 01911 }
static void dump_queue_members | ( | struct call_queue * | pm_queue | ) | [static] |
Definition at line 3579 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().
03580 { 03581 struct member *cur_member; 03582 char value[PM_MAX_LEN]; 03583 int value_len = 0; 03584 int res; 03585 struct ao2_iterator mem_iter; 03586 03587 memset(value, 0, sizeof(value)); 03588 03589 if (!pm_queue) 03590 return; 03591 03592 mem_iter = ao2_iterator_init(pm_queue->members, 0); 03593 while ((cur_member = ao2_iterator_next(&mem_iter))) { 03594 if (!cur_member->dynamic) { 03595 ao2_ref(cur_member, -1); 03596 continue; 03597 } 03598 03599 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s", 03600 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface); 03601 03602 ao2_ref(cur_member, -1); 03603 03604 if (res != strlen(value + value_len)) { 03605 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 03606 break; 03607 } 03608 value_len += res; 03609 } 03610 ao2_iterator_destroy(&mem_iter); 03611 03612 if (value_len && !cur_member) { 03613 if (ast_db_put(pm_family, pm_queue->name, value)) 03614 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 03615 } else 03616 /* Delete the entry if the queue is empty or there is an error */ 03617 ast_db_del(pm_family, pm_queue->name); 03618 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static, read] |
find the entry with the best metric, or NULL
Definition at line 2126 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
Referenced by ring_one(), and store_next().
02127 { 02128 struct callattempt *best = NULL, *cur; 02129 02130 for (cur = outgoing; cur; cur = cur->q_next) { 02131 if (cur->stillgoing && /* Not already done */ 02132 !cur->chan && /* Isn't already going */ 02133 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 02134 best = cur; 02135 } 02136 } 02137 02138 return best; 02139 }
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.
Definition at line 1253 of file app_queue.c.
References alloc_queue(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), ast_category_browse(), ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_variable_retrieve(), clear_queue(), call_queue::count, member::dead, call_queue::dead, init_queue(), member_interface::interface, LOG_DEBUG, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, member::realtime, call_queue::realtime, remove_from_interfaces(), remove_queue(), rr_dep_warning(), rt_handle_member_record(), S_OR, member::state_interface, strat2int(), call_queue::strategy, and ast_variable::value.
Referenced by load_realtime_queue().
01254 { 01255 struct ast_variable *v; 01256 struct call_queue *q; 01257 struct member *m; 01258 struct ao2_iterator mem_iter; 01259 char *interface = NULL; 01260 char *tmp, *tmp_name; 01261 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 01262 01263 /* Find the queue in the in-core list (we will create a new one if not found). */ 01264 AST_LIST_TRAVERSE(&queues, q, list) { 01265 if (!strcasecmp(q->name, queuename)) 01266 break; 01267 } 01268 01269 /* Static queues override realtime. */ 01270 if (q) { 01271 ao2_lock(q); 01272 if (!q->realtime) { 01273 if (q->dead) { 01274 ao2_unlock(q); 01275 return NULL; 01276 } else { 01277 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 01278 ao2_unlock(q); 01279 return q; 01280 } 01281 } 01282 } else if (!member_config) 01283 /* Not found in the list, and it's not realtime ... */ 01284 return NULL; 01285 01286 /* Check if queue is defined in realtime. */ 01287 if (!queue_vars) { 01288 /* Delete queue from in-core list if it has been deleted in realtime. */ 01289 if (q) { 01290 /*! \note Hmm, can't seem to distinguish a DB failure from a not 01291 found condition... So we might delete an in-core queue 01292 in case of DB failure. */ 01293 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename); 01294 01295 q->dead = 1; 01296 /* Delete if unused (else will be deleted when last caller leaves). */ 01297 if (!q->count) { 01298 /* Delete. */ 01299 ao2_unlock(q); 01300 remove_queue(q); 01301 } else 01302 ao2_unlock(q); 01303 } 01304 return NULL; 01305 } 01306 01307 /* Create a new queue if an in-core entry does not exist yet. */ 01308 if (!q) { 01309 struct ast_variable *tmpvar; 01310 if (!(q = alloc_queue(queuename))) 01311 return NULL; 01312 ao2_lock(q); 01313 clear_queue(q); 01314 q->realtime = 1; 01315 AST_LIST_INSERT_HEAD(&queues, q, list); 01316 01317 /* Due to the fact that the "rrordered" strategy will have a different allocation 01318 * scheme for queue members, we must devise the queue's strategy before other initializations. 01319 * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2 01320 * container used will have only a single bucket instead of the typical number. 01321 */ 01322 for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) { 01323 if (!strcasecmp(tmpvar->name, "strategy")) { 01324 q->strategy = strat2int(tmpvar->value); 01325 if (q->strategy < 0) { 01326 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01327 tmpvar->value, q->name); 01328 q->strategy = QUEUE_STRATEGY_RINGALL; 01329 } 01330 break; 01331 } 01332 } 01333 /* We traversed all variables and didn't find a strategy */ 01334 if (!tmpvar) { 01335 q->strategy = QUEUE_STRATEGY_RINGALL; 01336 } 01337 } 01338 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 01339 01340 memset(tmpbuf, 0, sizeof(tmpbuf)); 01341 for (v = queue_vars; v; v = v->next) { 01342 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 01343 if ((tmp = strchr(v->name, '_'))) { 01344 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 01345 tmp_name = tmpbuf; 01346 tmp = tmp_name; 01347 while ((tmp = strchr(tmp, '_'))) 01348 *tmp++ = '-'; 01349 } else 01350 tmp_name = v->name; 01351 01352 /* NULL values don't get returned from realtime; blank values should 01353 * still get set. If someone doesn't want a value to be set, they 01354 * should set the realtime column to NULL, not blank. */ 01355 queue_set_param(q, tmp_name, v->value, -1, 0); 01356 } 01357 01358 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 01359 rr_dep_warning(); 01360 01361 /* Temporarily set realtime members dead so we can detect deleted ones. 01362 * Also set the membercount correctly for realtime*/ 01363 mem_iter = ao2_iterator_init(q->members, 0); 01364 while ((m = ao2_iterator_next(&mem_iter))) { 01365 q->membercount++; 01366 if (m->realtime) 01367 m->dead = 1; 01368 ao2_ref(m, -1); 01369 } 01370 ao2_iterator_destroy(&mem_iter); 01371 01372 while ((interface = ast_category_browse(member_config, interface))) { 01373 rt_handle_member_record(q, interface, 01374 ast_variable_retrieve(member_config, interface, "membername"), 01375 ast_variable_retrieve(member_config, interface, "penalty"), 01376 ast_variable_retrieve(member_config, interface, "paused"), 01377 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface)); 01378 } 01379 01380 /* Delete all realtime members that have been deleted in DB. */ 01381 mem_iter = ao2_iterator_init(q->members, 0); 01382 while ((m = ao2_iterator_next(&mem_iter))) { 01383 if (m->dead) { 01384 ao2_unlink(q->members, m); 01385 ao2_unlock(q); 01386 remove_from_interfaces(m->state_interface); 01387 ao2_lock(q); 01388 q->membercount--; 01389 } 01390 ao2_ref(m, -1); 01391 } 01392 ao2_iterator_destroy(&mem_iter); 01393 01394 ao2_unlock(q); 01395 01396 return q; 01397 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Definition at line 1233 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), member::dynamic, call_queue::membercount, call_queue::members, remove_from_interfaces(), and member::state_interface.
Referenced by destroy_queue().
01234 { 01235 /* Free non-dynamic members */ 01236 struct member *cur; 01237 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01238 01239 while ((cur = ao2_iterator_next(&mem_iter))) { 01240 if (all || !cur->dynamic) { 01241 ao2_unlink(q->members, cur); 01242 remove_from_interfaces(cur->state_interface); 01243 q->membercount--; 01244 } 01245 ao2_ref(cur, -1); 01246 } 01247 ao2_iterator_destroy(&mem_iter); 01248 }
static enum queue_member_status get_member_status | ( | struct call_queue * | q, | |
int | max_penalty | |||
) | [static] |
Check if members are available.
This function checks to see if members are available to be called. If any member is available, the function immediately returns QUEUE_NORMAL. If no members are available, the appropriate reason why is returned
Definition at line 589 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL, and member::status.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
00590 { 00591 struct member *member; 00592 struct ao2_iterator mem_iter; 00593 enum queue_member_status result = QUEUE_NO_MEMBERS; 00594 int allpaused = 1, empty = 1; 00595 00596 ao2_lock(q); 00597 mem_iter = ao2_iterator_init(q->members, 0); 00598 while ((member = ao2_iterator_next(&mem_iter))) { 00599 empty = 0; 00600 00601 if (max_penalty && (member->penalty > max_penalty)) { 00602 ao2_ref(member, -1); 00603 continue; 00604 } 00605 00606 if (member->paused) { 00607 ao2_ref(member, -1); 00608 continue; 00609 } else { 00610 allpaused = 0; 00611 } 00612 00613 switch (member->status) { 00614 case AST_DEVICE_INVALID: 00615 /* nothing to do */ 00616 ao2_ref(member, -1); 00617 break; 00618 case AST_DEVICE_UNAVAILABLE: 00619 result = QUEUE_NO_REACHABLE_MEMBERS; 00620 ao2_ref(member, -1); 00621 break; 00622 default: 00623 ao2_unlock(q); 00624 ao2_ref(member, -1); 00625 return QUEUE_NORMAL; 00626 } 00627 } 00628 ao2_iterator_destroy(&mem_iter); 00629 ao2_unlock(q); 00630 00631 if (!empty && allpaused) { 00632 result = QUEUE_NO_REACHABLE_MEMBERS; 00633 } 00634 return result; 00635 }
static int handle_queue_add_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 5460 of file app_queue.c.
References add_to_queue(), ast_cli(), ast_queue_log(), member_interface::interface, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.
05461 { 05462 char *queuename, *interface, *membername = NULL, *state_interface = NULL; 05463 int penalty; 05464 05465 if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) { 05466 return RESULT_SHOWUSAGE; 05467 } else if (strcmp(argv[4], "to")) { 05468 return RESULT_SHOWUSAGE; 05469 } else if ((argc == 8) && strcmp(argv[6], "penalty")) { 05470 return RESULT_SHOWUSAGE; 05471 } else if ((argc == 10) && strcmp(argv[8], "as")) { 05472 return RESULT_SHOWUSAGE; 05473 } else if ((argc == 12) && strcmp(argv[10], "state_interface")) { 05474 return RESULT_SHOWUSAGE; 05475 } 05476 05477 queuename = argv[5]; 05478 interface = argv[3]; 05479 if (argc >= 8) { 05480 if (sscanf(argv[7], "%30d", &penalty) == 1) { 05481 if (penalty < 0) { 05482 ast_cli(fd, "Penalty must be >= 0\n"); 05483 penalty = 0; 05484 } 05485 } else { 05486 ast_cli(fd, "Penalty must be an integer >= 0\n"); 05487 penalty = 0; 05488 } 05489 } else { 05490 penalty = 0; 05491 } 05492 05493 if (argc >= 10) { 05494 membername = argv[9]; 05495 } 05496 05497 if (argc >= 12) { 05498 state_interface = argv[11]; 05499 } 05500 05501 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) { 05502 case RES_OKAY: 05503 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 05504 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 05505 return RESULT_SUCCESS; 05506 case RES_EXISTS: 05507 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 05508 return RESULT_FAILURE; 05509 case RES_NOSUCHQUEUE: 05510 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 05511 return RESULT_FAILURE; 05512 case RES_OUTOFMEMORY: 05513 ast_cli(fd, "Out of memory\n"); 05514 return RESULT_FAILURE; 05515 default: 05516 return RESULT_FAILURE; 05517 } 05518 }
static int handle_queue_remove_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 5553 of file app_queue.c.
References ast_cli(), ast_queue_log(), member_interface::interface, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.
05554 { 05555 char *queuename, *interface; 05556 05557 if (argc != 6) { 05558 return RESULT_SHOWUSAGE; 05559 } else if (strcmp(argv[4], "from")) { 05560 return RESULT_SHOWUSAGE; 05561 } 05562 05563 queuename = argv[5]; 05564 interface = argv[3]; 05565 05566 switch (remove_from_queue(queuename, interface)) { 05567 case RES_OKAY: 05568 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 05569 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 05570 return RESULT_SUCCESS; 05571 case RES_EXISTS: 05572 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 05573 return RESULT_FAILURE; 05574 case RES_NOSUCHQUEUE: 05575 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 05576 return RESULT_FAILURE; 05577 case RES_OUTOFMEMORY: 05578 ast_cli(fd, "Out of memory\n"); 05579 return RESULT_FAILURE; 05580 case RES_NOT_DYNAMIC: 05581 ast_cli(fd, "Member not dynamic\n"); 05582 return RESULT_FAILURE; 05583 default: 05584 return RESULT_FAILURE; 05585 } 05586 }
static void* handle_statechange | ( | struct statechange * | sc | ) | [static] |
set a member's status based on device state of that member's interface
Definition at line 697 of file app_queue.c.
References ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdupa, statechange::dev, devstate2str(), member_interface::interface, LOG_DEBUG, option_debug, statechange::state, and update_status().
Referenced by device_state_thread().
00698 { 00699 struct member_interface *curint; 00700 char *loc; 00701 char *technology; 00702 char interface[80]; 00703 00704 technology = ast_strdupa(sc->dev); 00705 loc = strchr(technology, '/'); 00706 if (loc) { 00707 *loc++ = '\0'; 00708 } else { 00709 return NULL; 00710 } 00711 00712 AST_LIST_LOCK(&interfaces); 00713 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00714 char *slash_pos; 00715 ast_copy_string(interface, curint->interface, sizeof(interface)); 00716 if ((slash_pos = strchr(interface, '/'))) 00717 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00718 *slash_pos = '\0'; 00719 00720 if (!strcasecmp(interface, sc->dev)) 00721 break; 00722 } 00723 AST_LIST_UNLOCK(&interfaces); 00724 00725 if (!curint) { 00726 if (option_debug > 2) 00727 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state)); 00728 return NULL; 00729 } 00730 00731 if (option_debug) 00732 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state)); 00733 00734 update_status(sc->dev, sc->state); 00735 00736 return NULL; 00737 }
static void hangupcalls | ( | struct callattempt * | outgoing, | |
struct ast_channel * | exception | |||
) | [static] |
Definition at line 1806 of file app_queue.c.
References ao2_ref(), ast_hangup(), callattempt::chan, free, callattempt::member, and callattempt::q_next.
Referenced by try_calling().
01807 { 01808 struct callattempt *oo; 01809 01810 while (outgoing) { 01811 /* Hangup any existing lines we have open */ 01812 if (outgoing->chan && (outgoing->chan != exception)) 01813 ast_hangup(outgoing->chan); 01814 oo = outgoing; 01815 outgoing = outgoing->q_next; 01816 if (oo->member) 01817 ao2_ref(oo->member, -1); 01818 free(oo); 01819 } 01820 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 873 of file app_queue.c.
References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ao2_container_alloc(), ast_copy_string(), call_queue::autofill, call_queue::context, call_queue::dead, DEFAULT_RETRY, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::moh, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_RRORDERED, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, call_queue::strategy, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_queues().
00874 { 00875 int i; 00876 00877 q->dead = 0; 00878 q->retry = DEFAULT_RETRY; 00879 q->timeout = -1; 00880 q->maxlen = 0; 00881 q->ringlimit = 0; 00882 q->announcefrequency = 0; 00883 q->announceholdtime = 0; 00884 q->roundingseconds = 0; /* Default - don't announce seconds */ 00885 q->servicelevel = 0; 00886 q->ringinuse = 1; 00887 q->setinterfacevar = 0; 00888 q->autofill = autofill_default; 00889 q->montype = montype_default; 00890 q->moh[0] = '\0'; 00891 q->announce[0] = '\0'; 00892 q->context[0] = '\0'; 00893 q->monfmt[0] = '\0'; 00894 q->periodicannouncefrequency = 0; 00895 q->reportholdtime = 0; 00896 q->monjoin = 0; 00897 q->wrapuptime = 0; 00898 q->joinempty = 0; 00899 q->leavewhenempty = 0; 00900 q->memberdelay = 0; 00901 q->maskmemberstatus = 0; 00902 q->eventwhencalled = 0; 00903 q->weight = 0; 00904 q->timeoutrestart = 0; 00905 if (!q->members) { 00906 if (q->strategy == QUEUE_STRATEGY_RRORDERED) { 00907 q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn); 00908 } else { 00909 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 00910 } 00911 } 00912 q->membercount = 0; 00913 q->found = 1; 00914 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); 00915 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare)); 00916 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls)); 00917 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime)); 00918 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes)); 00919 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds)); 00920 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks)); 00921 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan)); 00922 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold)); 00923 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0])); 00924 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 00925 q->sound_periodicannounce[i][0]='\0'; 00926 } 00927 }
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 553 of file app_queue.c.
References ao2_ref(), call_queue::head, and queue_ent::next.
Referenced by join_queue().
00554 { 00555 struct queue_ent *cur; 00556 00557 if (!q || !new) 00558 return; 00559 if (prev) { 00560 cur = prev->next; 00561 prev->next = new; 00562 } else { 00563 cur = q->head; 00564 q->head = new; 00565 } 00566 new->next = cur; 00567 00568 /* every queue_ent must have a reference to it's parent call_queue, this 00569 * reference does not go away until the end of the queue_ent's life, meaning 00570 * that even when the queue_ent leaves the call_queue this ref must remain. */ 00571 ao2_ref(q, +1); 00572 new->parent = q; 00573 new->pos = ++(*pos); 00574 new->opos = *pos; 00575 }
static char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 507 of file app_queue.c.
References strategy::name, and strategies.
Referenced by __queues_show().
00508 { 00509 int x; 00510 00511 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) { 00512 if (strategy == strategies[x].strategy) 00513 return strategies[x].name; 00514 } 00515 00516 return "<unknown>"; 00517 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static, read] |
Definition at line 3552 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(), and set_member_paused().
03553 { 03554 struct member *mem; 03555 struct ao2_iterator mem_iter; 03556 03557 if (!q) 03558 return NULL; 03559 03560 mem_iter = ao2_iterator_init(q->members, 0); 03561 while ((mem = ao2_iterator_next(&mem_iter))) { 03562 if (!strcasecmp(interface, mem->interface)) { 03563 ao2_iterator_destroy(&mem_iter); 03564 return mem; 03565 } 03566 ao2_ref(mem, -1); 03567 } 03568 ao2_iterator_destroy(&mem_iter); 03569 03570 return NULL; 03571 }
static int interface_exists_global | ( | const char * | interface | ) | [static] |
Definition at line 965 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, call_queue::members, and member::state_interface.
Referenced by remove_from_interfaces().
00966 { 00967 struct call_queue *q; 00968 struct member *mem; 00969 struct ao2_iterator mem_iter; 00970 int ret = 0; 00971 00972 AST_LIST_LOCK(&queues); 00973 AST_LIST_TRAVERSE(&queues, q, list) { 00974 ao2_lock(q); 00975 mem_iter = ao2_iterator_init(q->members, 0); 00976 while ((mem = ao2_iterator_next(&mem_iter))) { 00977 if (!strcasecmp(mem->state_interface, interface)) { 00978 ao2_ref(mem, -1); 00979 ret = 1; 00980 break; 00981 } 00982 ao2_ref(mem, -1); 00983 } 00984 ao2_iterator_destroy(&mem_iter); 00985 ao2_unlock(q); 00986 if (ret) 00987 break; 00988 } 00989 AST_LIST_UNLOCK(&queues); 00990 00991 return ret; 00992 }
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 2590 of file app_queue.c.
References ao2_lock(), ao2_unlock(), ast_log(), call_queue::autofill, queue_ent::chan, call_queue::head, LOG_DEBUG, queue_ent::next, num_available_members(), option_debug, queue_ent::parent, queue_ent::pending, and queue_ent::pos.
Referenced by queue_exec(), and wait_our_turn().
02591 { 02592 struct queue_ent *ch; 02593 int res; 02594 int avl; 02595 int idx = 0; 02596 /* This needs a lock. How many members are available to be served? */ 02597 ao2_lock(qe->parent); 02598 02599 avl = num_available_members(qe->parent); 02600 02601 ch = qe->parent->head; 02602 02603 if (option_debug) { 02604 ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member"); 02605 } 02606 02607 while ((idx < avl) && (ch) && (ch != qe)) { 02608 if (!ch->pending) 02609 idx++; 02610 ch = ch->next; 02611 } 02612 02613 ao2_unlock(qe->parent); 02614 /* If the queue entry is within avl [the number of available members] calls from the top ... 02615 * Autofill and position check added to support autofill=no (as only calls 02616 * from the front of the queue are valid when autofill is disabled) 02617 */ 02618 if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) { 02619 if (option_debug) 02620 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02621 res = 1; 02622 } else { 02623 if (option_debug) 02624 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02625 res = 0; 02626 } 02627 02628 return res; 02629 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason | |||
) | [static] |
Definition at line 1520 of file app_queue.c.
References call_queue::announce, queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), 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(), LOG_DEBUG, manager_event(), queue_ent::max_penalty, call_queue::maxlen, call_queue::moh, queue_ent::moh, call_queue::name, queue_ent::next, option_debug, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, and S_OR.
Referenced by queue_exec().
01521 { 01522 struct call_queue *q; 01523 struct queue_ent *cur, *prev = NULL; 01524 int res = -1; 01525 int pos = 0; 01526 int inserted = 0; 01527 enum queue_member_status stat; 01528 01529 if (!(q = load_realtime_queue(queuename))) 01530 return res; 01531 01532 AST_LIST_LOCK(&queues); 01533 ao2_lock(q); 01534 01535 /* This is our one */ 01536 stat = get_member_status(q, qe->max_penalty); 01537 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS)) 01538 *reason = QUEUE_JOINEMPTY; 01539 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS)) 01540 *reason = QUEUE_JOINUNAVAIL; 01541 else if (q->maxlen && (q->count >= q->maxlen)) 01542 *reason = QUEUE_FULL; 01543 else { 01544 /* There's space for us, put us at the right position inside 01545 * the queue. 01546 * Take into account the priority of the calling user */ 01547 inserted = 0; 01548 prev = NULL; 01549 cur = q->head; 01550 while (cur) { 01551 /* We have higher priority than the current user, enter 01552 * before him, after all the other users with priority 01553 * higher or equal to our priority. */ 01554 if ((!inserted) && (qe->prio > cur->prio)) { 01555 insert_entry(q, prev, qe, &pos); 01556 inserted = 1; 01557 } 01558 cur->pos = ++pos; 01559 prev = cur; 01560 cur = cur->next; 01561 } 01562 /* No luck, join at the end of the queue */ 01563 if (!inserted) 01564 insert_entry(q, prev, qe, &pos); 01565 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 01566 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 01567 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 01568 q->count++; 01569 res = 0; 01570 manager_event(EVENT_FLAG_CALL, "Join", 01571 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n", 01572 qe->chan->name, 01573 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */ 01574 S_OR(qe->chan->cid.cid_name, "unknown"), 01575 q->name, qe->pos, q->count, qe->chan->uniqueid ); 01576 if (option_debug) 01577 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 01578 } 01579 ao2_unlock(q); 01580 AST_LIST_UNLOCK(&queues); 01581 01582 return res; 01583 }
static void leave_queue | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1765 of file app_queue.c.
References ao2_lock(), ao2_unlock(), ast_log(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, LOG_DEBUG, manager_event(), call_queue::name, queue_ent::next, option_debug, queue_ent::parent, queue_ent::pos, and remove_queue().
Referenced by queue_exec(), try_calling(), and wait_our_turn().
01766 { 01767 struct call_queue *q; 01768 struct queue_ent *cur, *prev = NULL; 01769 int pos = 0; 01770 01771 if (!(q = qe->parent)) 01772 return; 01773 ao2_lock(q); 01774 01775 prev = NULL; 01776 for (cur = q->head; cur; cur = cur->next) { 01777 if (cur == qe) { 01778 q->count--; 01779 01780 /* Take us out of the queue */ 01781 manager_event(EVENT_FLAG_CALL, "Leave", 01782 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", 01783 qe->chan->name, q->name, q->count, qe->chan->uniqueid); 01784 if (option_debug) 01785 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 01786 /* Take us out of the queue */ 01787 if (prev) 01788 prev->next = cur->next; 01789 else 01790 q->head = cur->next; 01791 } else { 01792 /* Renumber the people after us in the queue based on a new count */ 01793 cur->pos = ++pos; 01794 prev = cur; 01795 } 01796 } 01797 ao2_unlock(q); 01798 01799 if (q->dead && !q->count) { 01800 /* It's dead and nobody is in it, so kill it */ 01801 remove_queue(q); 01802 } 01803 }
static int load_module | ( | void | ) | [static] |
Definition at line 5845 of file app_queue.c.
References aqm_exec(), ast_cli_register_multiple(), ast_cond_init(), ast_custom_function_register(), ast_devstate_add(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_mutex_init(), ast_pthread_create, ast_register_application(), cli_queue, device_state, device_state_thread(), EVENT_FLAG_AGENT, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_member_count(), manager_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), ql_exec(), queue_exec(), queueagentcount_function, queueexists_function, queuemembercount_function, queuememberlist_function, queuememberpaused_function, queuememberstatus_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().
05846 { 05847 int res; 05848 05849 if (!reload_queues()) 05850 return AST_MODULE_LOAD_DECLINE; 05851 05852 if (queue_persistent_members) 05853 reload_queue_members(); 05854 05855 ast_mutex_init(&device_state.lock); 05856 ast_cond_init(&device_state.cond, NULL); 05857 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL); 05858 05859 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 05860 res = ast_register_application(app, queue_exec, synopsis, descrip); 05861 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip); 05862 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip); 05863 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip); 05864 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip); 05865 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip); 05866 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); 05867 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); 05868 res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count"); 05869 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); 05870 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); 05871 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); 05872 res |= ast_custom_function_register(&queueexists_function); 05873 res |= ast_custom_function_register(&queueagentcount_function); 05874 res |= ast_custom_function_register(&queuemembercount_function); 05875 res |= ast_custom_function_register(&queuememberlist_function); 05876 res |= ast_custom_function_register(&queuememberstatus_function); 05877 res |= ast_custom_function_register(&queuememberpaused_function); 05878 res |= ast_custom_function_register(&queuewaitingcount_function); 05879 res |= ast_devstate_add(statechange_queue, NULL); 05880 05881 return res; 05882 }
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 1470 of file app_queue.c.
References ast_config_destroy(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, call_queue::realtime, and update_realtime_members().
Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_exists(), queue_function_queuemembercount(), queue_member_count(), and reload_queue_members().
01471 { 01472 struct ast_variable *queue_vars; 01473 struct ast_config *member_config = NULL; 01474 struct call_queue *q; 01475 01476 /* Find the queue in the in-core list first. */ 01477 AST_LIST_LOCK(&queues); 01478 AST_LIST_TRAVERSE(&queues, q, list) { 01479 if (!strcasecmp(q->name, queuename)) { 01480 break; 01481 } 01482 } 01483 AST_LIST_UNLOCK(&queues); 01484 01485 if (!q || q->realtime) { 01486 /*! \note Load from realtime before taking the global qlock, to avoid blocking all 01487 queue operations while waiting for the DB. 01488 01489 This will be two separate database transactions, so we might 01490 see queue parameters as they were before another process 01491 changed the queue and member list as it was after the change. 01492 Thus we might see an empty member list when a queue is 01493 deleted. In practise, this is unlikely to cause a problem. */ 01494 01495 queue_vars = ast_load_realtime("queues", "name", queuename, NULL); 01496 if (queue_vars) { 01497 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL); 01498 if (!member_config) { 01499 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n"); 01500 ast_variables_destroy(queue_vars); 01501 return NULL; 01502 } 01503 } 01504 01505 AST_LIST_LOCK(&queues); 01506 01507 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 01508 if (member_config) 01509 ast_config_destroy(member_config); 01510 if (queue_vars) 01511 ast_variables_destroy(queue_vars); 01512 01513 AST_LIST_UNLOCK(&queues); 01514 } else { 01515 update_realtime_members(q); 01516 } 01517 return q; 01518 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5352 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(), member_interface::interface, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.
Referenced by load_module().
05353 { 05354 const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface; 05355 int paused, penalty = 0; 05356 05357 queuename = astman_get_header(m, "Queue"); 05358 interface = astman_get_header(m, "Interface"); 05359 penalty_s = astman_get_header(m, "Penalty"); 05360 paused_s = astman_get_header(m, "Paused"); 05361 membername = astman_get_header(m, "MemberName"); 05362 state_interface = astman_get_header(m, "StateInterface"); 05363 05364 if (ast_strlen_zero(queuename)) { 05365 astman_send_error(s, m, "'Queue' not specified."); 05366 return 0; 05367 } 05368 05369 if (ast_strlen_zero(interface)) { 05370 astman_send_error(s, m, "'Interface' not specified."); 05371 return 0; 05372 } 05373 05374 if (ast_strlen_zero(penalty_s)) 05375 penalty = 0; 05376 else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0) 05377 penalty = 0; 05378 05379 if (ast_strlen_zero(paused_s)) 05380 paused = 0; 05381 else 05382 paused = abs(ast_true(paused_s)); 05383 05384 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) { 05385 case RES_OKAY: 05386 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 05387 astman_send_ack(s, m, "Added interface to queue"); 05388 break; 05389 case RES_EXISTS: 05390 astman_send_error(s, m, "Unable to add interface: Already there"); 05391 break; 05392 case RES_NOSUCHQUEUE: 05393 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 05394 break; 05395 case RES_OUTOFMEMORY: 05396 astman_send_error(s, m, "Out of memory"); 05397 break; 05398 } 05399 05400 return 0; 05401 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5437 of file app_queue.c.
References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), member_interface::interface, and set_member_paused().
Referenced by load_module().
05438 { 05439 const char *queuename, *interface, *paused_s; 05440 int paused; 05441 05442 interface = astman_get_header(m, "Interface"); 05443 paused_s = astman_get_header(m, "Paused"); 05444 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 05445 05446 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 05447 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 05448 return 0; 05449 } 05450 05451 paused = abs(ast_true(paused_s)); 05452 05453 if (set_member_paused(queuename, interface, paused)) 05454 astman_send_error(s, m, "Interface not found"); 05455 else 05456 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 05457 return 0; 05458 }
static int manager_queue_member_count | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5697 of file app_queue.c.
References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), qmc_handler(), and RESULT_SUCCESS.
Referenced by load_module().
05698 { 05699 char buffer[256] = ""; 05700 const char *queuename = astman_get_header(m,"Queue"); 05701 05702 if (ast_strlen_zero(queuename)) { 05703 astman_send_error(s, m, "'Queue' not specified."); 05704 return 0; 05705 } 05706 if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) { 05707 astman_send_ack(s, m, buffer); 05708 return RESULT_SUCCESS; 05709 } else { 05710 astman_send_error(s, m, "Queue not found."); 05711 return 0; 05712 } 05713 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5246 of file app_queue.c.
References __queues_show(), astman_append(), and RESULT_SUCCESS.
Referenced by load_module().
05247 { 05248 char *a[] = { "queue", "show" }; 05249 05250 __queues_show(s, 1, -1, 2, a); 05251 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 05252 05253 return RESULT_SUCCESS; 05254 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5257 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_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, member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, RESULT_SUCCESS, call_queue::ringlimit, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.
Referenced by load_module().
05258 { 05259 time_t now; 05260 int pos; 05261 const char *id = astman_get_header(m,"ActionID"); 05262 const char *queuefilter = astman_get_header(m,"Queue"); 05263 const char *memberfilter = astman_get_header(m,"Member"); 05264 char idText[256] = ""; 05265 struct call_queue *q; 05266 struct queue_ent *qe; 05267 float sl = 0; 05268 struct member *mem; 05269 struct ao2_iterator mem_iter; 05270 05271 astman_send_ack(s, m, "Queue status will follow"); 05272 time(&now); 05273 AST_LIST_LOCK(&queues); 05274 if (!ast_strlen_zero(id)) 05275 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 05276 05277 AST_LIST_TRAVERSE(&queues, q, list) { 05278 ao2_lock(q); 05279 05280 /* List queue properties */ 05281 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 05282 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 05283 astman_append(s, "Event: QueueParams\r\n" 05284 "Queue: %s\r\n" 05285 "Max: %d\r\n" 05286 "Calls: %d\r\n" 05287 "Holdtime: %d\r\n" 05288 "Completed: %d\r\n" 05289 "Abandoned: %d\r\n" 05290 "ServiceLevel: %d\r\n" 05291 "ServicelevelPerf: %2.1f\r\n" 05292 "RingLimit: %d\r\n" 05293 "Weight: %d\r\n" 05294 "%s" 05295 "\r\n", 05296 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, 05297 q->callsabandoned, q->servicelevel, sl, q->ringlimit, q->weight, idText); 05298 /* List Queue Members */ 05299 mem_iter = ao2_iterator_init(q->members, 0); 05300 while ((mem = ao2_iterator_next(&mem_iter))) { 05301 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) { 05302 astman_append(s, "Event: QueueMember\r\n" 05303 "Queue: %s\r\n" 05304 "Name: %s\r\n" 05305 "Location: %s\r\n" 05306 "Membership: %s\r\n" 05307 "Penalty: %d\r\n" 05308 "CallsTaken: %d\r\n" 05309 "LastCall: %d\r\n" 05310 "Status: %d\r\n" 05311 "Paused: %d\r\n" 05312 "%s" 05313 "\r\n", 05314 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 05315 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 05316 } 05317 ao2_ref(mem, -1); 05318 } 05319 ao2_iterator_destroy(&mem_iter); 05320 /* List Queue Entries */ 05321 pos = 1; 05322 for (qe = q->head; qe; qe = qe->next) { 05323 astman_append(s, "Event: QueueEntry\r\n" 05324 "Queue: %s\r\n" 05325 "Position: %d\r\n" 05326 "Channel: %s\r\n" 05327 "CallerID: %s\r\n" 05328 "CallerIDName: %s\r\n" 05329 "Wait: %ld\r\n" 05330 "%s" 05331 "\r\n", 05332 q->name, pos++, qe->chan->name, 05333 S_OR(qe->chan->cid.cid_num, "unknown"), 05334 S_OR(qe->chan->cid.cid_name, "unknown"), 05335 (long) (now - qe->start), idText); 05336 } 05337 } 05338 ao2_unlock(q); 05339 } 05340 05341 astman_append(s, 05342 "Event: QueueStatusComplete\r\n" 05343 "%s" 05344 "\r\n",idText); 05345 05346 AST_LIST_UNLOCK(&queues); 05347 05348 05349 return RESULT_SUCCESS; 05350 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 5403 of file app_queue.c.
References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), member_interface::interface, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and RES_OUTOFMEMORY.
Referenced by load_module().
05404 { 05405 const char *queuename, *interface; 05406 05407 queuename = astman_get_header(m, "Queue"); 05408 interface = astman_get_header(m, "Interface"); 05409 05410 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 05411 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 05412 return 0; 05413 } 05414 05415 switch (remove_from_queue(queuename, interface)) { 05416 case RES_OKAY: 05417 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 05418 astman_send_ack(s, m, "Removed interface from queue"); 05419 break; 05420 case RES_EXISTS: 05421 astman_send_error(s, m, "Unable to remove interface: Not there"); 05422 break; 05423 case RES_NOSUCHQUEUE: 05424 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 05425 break; 05426 case RES_OUTOFMEMORY: 05427 astman_send_error(s, m, "Out of memory"); 05428 break; 05429 case RES_NOT_DYNAMIC: 05430 astman_send_error(s, m, "Member not dynamic"); 05431 break; 05432 } 05433 05434 return 0; 05435 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 867 of file app_queue.c.
References member::interface.
Referenced by init_queue().
static int member_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 855 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
00856 { 00857 const struct member *mem = obj; 00858 const char *chname = strchr(mem->interface, '/'); 00859 int ret = 0, i; 00860 if (!chname) 00861 chname = mem->interface; 00862 for (i = 0; i < 5 && chname[i]; i++) 00863 ret += compress_char(chname[i]) << (i * 6); 00864 return ret; 00865 }
static void monjoin_dep_warning | ( | void | ) | [static] |
Definition at line 486 of file app_queue.c.
References ast_log(), and LOG_NOTICE.
Referenced by queue_set_param().
00487 { 00488 static unsigned int warned = 0; 00489 if (!warned) { 00490 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n"); 00491 warned = 1; 00492 } 00493 }
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 1830 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_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, 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().
01831 { 01832 struct member *mem; 01833 int avl = 0; 01834 struct ao2_iterator mem_iter; 01835 01836 mem_iter = ao2_iterator_init(q->members, 0); 01837 while ((mem = ao2_iterator_next(&mem_iter))) { 01838 switch (mem->status) { 01839 case AST_DEVICE_INUSE: 01840 if (!q->ringinuse) 01841 break; 01842 /* else fall through */ 01843 case AST_DEVICE_NOT_INUSE: 01844 case AST_DEVICE_ONHOLD: 01845 case AST_DEVICE_RINGINUSE: 01846 case AST_DEVICE_RINGING: 01847 case AST_DEVICE_UNKNOWN: 01848 if (!mem->paused) { 01849 avl++; 01850 } 01851 break; 01852 } 01853 ao2_ref(mem, -1); 01854 01855 /* If autofill is not enabled or if the queue's strategy is ringall, then 01856 * we really don't care about the number of available members so much as we 01857 * do that there is at least one available. 01858 * 01859 * In fact, we purposely will return from this function stating that only 01860 * one member is available if either of those conditions hold. That way, 01861 * functions which determine what action to take based on the number of available 01862 * members will operate properly. The reasoning is that even if multiple 01863 * members are available, only the head caller can actually be serviced. 01864 */ 01865 if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) { 01866 break; 01867 } 01868 } 01869 ao2_iterator_destroy(&mem_iter); 01870 01871 return avl; 01872 }
static int play_file | ( | struct ast_channel * | chan, | |
char * | filename | |||
) | [static] |
Definition at line 1585 of file app_queue.c.
References AST_DIGIT_ANY, ast_fileexists(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), and ast_waitstream().
Referenced by say_periodic_announcement(), say_position(), and try_calling().
01586 { 01587 int res; 01588 01589 if (ast_strlen_zero(filename)) { 01590 return 0; 01591 } 01592 01593 if (!ast_fileexists(filename, NULL, chan->language)) { 01594 return 0; 01595 } 01596 01597 ast_stopstream(chan); 01598 01599 res = ast_streamfile(chan, filename, chan->language); 01600 if (!res) 01601 res = ast_waitstream(chan, AST_DIGIT_ANY); 01602 01603 ast_stopstream(chan); 01604 01605 return res; 01606 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3869 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().
Referenced by load_module().
03870 { 03871 struct ast_module_user *lu; 03872 char *parse; 03873 int priority_jump = 0; 03874 int ignore_fail = 0; 03875 AST_DECLARE_APP_ARGS(args, 03876 AST_APP_ARG(queuename); 03877 AST_APP_ARG(interface); 03878 AST_APP_ARG(options); 03879 ); 03880 03881 if (ast_strlen_zero(data)) { 03882 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03883 return -1; 03884 } 03885 03886 parse = ast_strdupa(data); 03887 03888 AST_STANDARD_APP_ARGS(args, parse); 03889 03890 lu = ast_module_user_add(chan); 03891 03892 if (args.options) { 03893 if (strchr(args.options, 'j')) 03894 priority_jump = 1; 03895 if (strchr(args.options, 'i')) 03896 ignore_fail = 1; 03897 } 03898 03899 if (ast_strlen_zero(args.interface)) { 03900 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03901 ast_module_user_remove(lu); 03902 return -1; 03903 } 03904 03905 if (set_member_paused(args.queuename, args.interface, 1)) { 03906 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 03907 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 03908 if (priority_jump || ast_opt_priority_jumping) { 03909 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03910 ast_module_user_remove(lu); 03911 return 0; 03912 } 03913 } 03914 ast_module_user_remove(lu); 03915 if (ignore_fail) { 03916 return 0; 03917 } else { 03918 return -1; 03919 } 03920 } 03921 03922 ast_module_user_remove(lu); 03923 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 03924 return 0; 03925 }
static int ql_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 4127 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_module_user_add, ast_module_user_remove, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), LOG_WARNING, and parse().
Referenced by load_module().
04128 { 04129 struct ast_module_user *u; 04130 char *parse; 04131 04132 AST_DECLARE_APP_ARGS(args, 04133 AST_APP_ARG(queuename); 04134 AST_APP_ARG(uniqueid); 04135 AST_APP_ARG(membername); 04136 AST_APP_ARG(event); 04137 AST_APP_ARG(params); 04138 ); 04139 04140 if (ast_strlen_zero(data)) { 04141 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n"); 04142 return -1; 04143 } 04144 04145 u = ast_module_user_add(chan); 04146 04147 parse = ast_strdupa(data); 04148 04149 AST_STANDARD_APP_ARGS(args, parse); 04150 04151 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 04152 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 04153 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n"); 04154 ast_module_user_remove(u); 04155 return -1; 04156 } 04157 04158 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 04159 "%s", args.params ? args.params : ""); 04160 04161 ast_module_user_remove(u); 04162 04163 return 0; 04164 }
static int qmc_handler | ( | const char * | queuename, | |
char * | buffer, | |||
int | len | |||
) | [static] |
Definition at line 5682 of file app_queue.c.
References member_count::active, member_count::all, member_count::free, member_count::inuse, member_count::paused, queue_member_count(), RESULT_FAILURE, RESULT_SUCCESS, and member_count::valid.
Referenced by cli_queue_member_count(), and manager_queue_member_count().
05683 { 05684 struct member_count qmc; 05685 memset(&qmc, 0, sizeof(qmc)); 05686 if (queue_member_count(queuename, &qmc) != 0) { 05687 return RESULT_FAILURE; 05688 } else { 05689 snprintf(buffer, len, 05690 "valid:%d inuse:%d paused:%d active:%d free:%d all:%d", 05691 qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all); 05692 return RESULT_SUCCESS; 05693 } 05694 }
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 uless some condition (such as an expiration time) causes us to exit the queue.
Definition at line 4178 of file app_queue.c.
References call_queue::announcefrequency, ao2_ref(), AST_APP_ARG, AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verbose(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::moh, queue_ent::opos, option_debug, option_verbose, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, stop, try_calling(), update_realtime_members(), queue_ent::valid_digits, VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().
Referenced by load_module().
04179 { 04180 int res=-1; 04181 int ringing=0; 04182 struct ast_module_user *lu; 04183 const char *user_priority; 04184 const char *max_penalty_str; 04185 int prio; 04186 int max_penalty; 04187 enum queue_result reason = QUEUE_UNKNOWN; 04188 /* whether to exit Queue application after the timeout hits */ 04189 int tries = 0; 04190 int noption = 0; 04191 char *parse; 04192 AST_DECLARE_APP_ARGS(args, 04193 AST_APP_ARG(queuename); 04194 AST_APP_ARG(options); 04195 AST_APP_ARG(url); 04196 AST_APP_ARG(announceoverride); 04197 AST_APP_ARG(queuetimeoutstr); 04198 AST_APP_ARG(agi); 04199 ); 04200 /* Our queue entry */ 04201 struct queue_ent qe = { 0 }; 04202 04203 if (ast_strlen_zero(data)) { 04204 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n"); 04205 return -1; 04206 } 04207 04208 parse = ast_strdupa(data); 04209 AST_STANDARD_APP_ARGS(args, parse); 04210 04211 lu = ast_module_user_add(chan); 04212 04213 /* Setup our queue entry */ 04214 qe.start = time(NULL); 04215 04216 /* set the expire time based on the supplied timeout; */ 04217 if (!ast_strlen_zero(args.queuetimeoutstr)) 04218 qe.expire = qe.start + atoi(args.queuetimeoutstr); 04219 else 04220 qe.expire = 0; 04221 04222 /* Get the priority from the variable ${QUEUE_PRIO} */ 04223 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 04224 if (user_priority) { 04225 if (sscanf(user_priority, "%30d", &prio) == 1) { 04226 if (option_debug) 04227 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", 04228 chan->name, prio); 04229 } else { 04230 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 04231 user_priority, chan->name); 04232 prio = 0; 04233 } 04234 } else { 04235 if (option_debug > 2) 04236 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); 04237 prio = 0; 04238 } 04239 04240 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 04241 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 04242 if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) { 04243 if (option_debug) 04244 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", 04245 chan->name, max_penalty); 04246 } else { 04247 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 04248 max_penalty_str, chan->name); 04249 max_penalty = 0; 04250 } 04251 } else { 04252 max_penalty = 0; 04253 } 04254 04255 if (args.options && (strchr(args.options, 'r'))) 04256 ringing = 1; 04257 04258 if (ringing != 1 && args.options && (strchr(args.options, 'R'))) { 04259 qe.ring_when_ringing = 1; 04260 } 04261 04262 if (option_debug) 04263 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 04264 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 04265 04266 qe.chan = chan; 04267 qe.prio = prio; 04268 qe.max_penalty = max_penalty; 04269 qe.last_pos_said = 0; 04270 qe.last_pos = 0; 04271 qe.last_periodic_announce_time = time(NULL); 04272 qe.last_periodic_announce_sound = 0; 04273 qe.valid_digits = 0; 04274 if (!join_queue(args.queuename, &qe, &reason)) { 04275 int makeannouncement = 0; 04276 04277 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), 04278 S_OR(chan->cid.cid_num, "")); 04279 check_turns: 04280 if (ringing) { 04281 ast_indicate(chan, AST_CONTROL_RINGING); 04282 } else { 04283 ast_moh_start(chan, qe.moh, NULL); 04284 } 04285 04286 /* This is the wait loop for callers 2 through maxlen */ 04287 res = wait_our_turn(&qe, ringing, &reason); 04288 if (res) 04289 goto stop; 04290 04291 for (;;) { 04292 /* This is the wait loop for the head caller*/ 04293 /* To exit, they may get their call answered; */ 04294 /* they may dial a digit from the queue context; */ 04295 /* or, they may timeout. */ 04296 04297 enum queue_member_status stat; 04298 04299 /* Leave if we have exceeded our queuetimeout */ 04300 if (qe.expire && (time(NULL) >= qe.expire)) { 04301 record_abandoned(&qe); 04302 reason = QUEUE_TIMEOUT; 04303 res = 0; 04304 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04305 break; 04306 } 04307 04308 if (makeannouncement) { 04309 /* Make a position announcement, if enabled */ 04310 if (qe.parent->announcefrequency && !ringing) 04311 if ((res = say_position(&qe))) 04312 goto stop; 04313 04314 } 04315 makeannouncement = 1; 04316 04317 /* Leave if we have exceeded our queuetimeout */ 04318 if (qe.expire && (time(NULL) >= qe.expire)) { 04319 record_abandoned(&qe); 04320 reason = QUEUE_TIMEOUT; 04321 res = 0; 04322 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04323 break; 04324 } 04325 /* Make a periodic announcement, if enabled */ 04326 if (qe.parent->periodicannouncefrequency && !ringing) 04327 if ((res = say_periodic_announcement(&qe))) 04328 goto stop; 04329 04330 /* Leave if we have exceeded our queuetimeout */ 04331 if (qe.expire && (time(NULL) >= qe.expire)) { 04332 record_abandoned(&qe); 04333 reason = QUEUE_TIMEOUT; 04334 res = 0; 04335 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04336 break; 04337 } 04338 /* Try calling all queue members for 'timeout' seconds */ 04339 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi); 04340 if (res) 04341 goto stop; 04342 04343 stat = get_member_status(qe.parent, qe.max_penalty); 04344 04345 /* exit after 'timeout' cycle if 'n' option enabled */ 04346 if (noption && tries >= qe.parent->membercount) { 04347 if (option_verbose > 2) 04348 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n"); 04349 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04350 record_abandoned(&qe); 04351 reason = QUEUE_TIMEOUT; 04352 res = 0; 04353 break; 04354 } 04355 04356 /* leave the queue if no agents, if enabled */ 04357 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 04358 record_abandoned(&qe); 04359 reason = QUEUE_LEAVEEMPTY; 04360 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04361 res = 0; 04362 break; 04363 } 04364 04365 /* leave the queue if no reachable agents, if enabled */ 04366 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 04367 record_abandoned(&qe); 04368 reason = QUEUE_LEAVEUNAVAIL; 04369 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04370 res = 0; 04371 break; 04372 } 04373 04374 /* Leave if we have exceeded our queuetimeout */ 04375 if (qe.expire && (time(NULL) >= qe.expire)) { 04376 record_abandoned(&qe); 04377 reason = QUEUE_TIMEOUT; 04378 res = 0; 04379 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04380 break; 04381 } 04382 04383 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 04384 update_realtime_members(qe.parent); 04385 04386 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 04387 res = wait_a_bit(&qe); 04388 if (res) 04389 goto stop; 04390 04391 /* Since this is a priority queue and 04392 * it is not sure that we are still at the head 04393 * of the queue, go and check for our turn again. 04394 */ 04395 if (!is_our_turn(&qe)) { 04396 if (option_debug) 04397 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n", 04398 qe.chan->name); 04399 goto check_turns; 04400 } 04401 } 04402 04403 stop: 04404 if (res) { 04405 if (res < 0) { 04406 if (!qe.handled) { 04407 record_abandoned(&qe); 04408 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 04409 "%d|%d|%ld", qe.pos, qe.opos, 04410 (long) time(NULL) - qe.start); 04411 } 04412 res = -1; 04413 } else if (qe.valid_digits) { 04414 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 04415 "%s|%d", qe.digits, qe.pos); 04416 } 04417 } 04418 04419 /* Don't allow return code > 0 */ 04420 if (res >= 0) { 04421 res = 0; 04422 if (ringing) { 04423 ast_indicate(chan, -1); 04424 } else { 04425 ast_moh_stop(chan); 04426 } 04427 ast_stopstream(chan); 04428 } 04429 leave_queue(&qe); 04430 if (reason != QUEUE_UNKNOWN) 04431 set_queue_result(chan, reason); 04432 } else { 04433 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 04434 set_queue_result(chan, reason); 04435 res = 0; 04436 } 04437 if (qe.parent) { 04438 /* every queue_ent is given a reference to it's parent call_queue when it joins the queue. 04439 * This ref must be taken away right before the queue_ent is destroyed. In this case 04440 * the queue_ent is about to be returned on the stack */ 04441 ao2_ref(qe.parent, -1); 04442 } 04443 ast_module_user_remove(lu); 04444 04445 return res; 04446 }
static int queue_function_exists | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4754 of file app_queue.c.
References ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), load_realtime_queue(), and LOG_ERROR.
04755 { 04756 struct ast_module_user *lu; 04757 buf[0] = '\0'; 04758 04759 if (ast_strlen_zero(data)) { 04760 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04761 return -1; 04762 } 04763 lu = ast_module_user_add(chan); 04764 snprintf(buf, len, "%d", load_realtime_queue(data) != NULL? 1 : 0); 04765 ast_module_user_remove(lu); 04766 04767 return 0; 04768 }
static int queue_function_queuemembercount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4456 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_DEVICE_UNKNOWN, ast_log(), ast_module_user_add, ast_module_user_remove, ast_strdupa, ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, LOG_WARNING, call_queue::members, name, member::paused, QMC_ACTIVE, QMC_ALL, QMC_FREE, QMC_PAUSED, QMC_VALID, and member::status.
04457 { 04458 int count = 0; 04459 struct call_queue *q; 04460 struct ast_module_user *lu; 04461 struct member *m; 04462 struct ao2_iterator mem_iter; 04463 char *name, *item; 04464 enum qmc_status mode = QMC_VALID; 04465 04466 buf[0] = '\0'; 04467 04468 if (ast_strlen_zero(data)) { 04469 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04470 return -1; 04471 } 04472 04473 name = ast_strdupa(data); 04474 04475 lu = ast_module_user_add(chan); 04476 04477 if ((item = strchr(name, ':'))) { 04478 *item = '\0'; 04479 item++; 04480 } else { 04481 item = ""; 04482 } 04483 04484 if (!strcasecmp(item, "valid")) { 04485 mode = QMC_VALID; 04486 } else if (!strcasecmp(item, "paused")) { 04487 mode = QMC_PAUSED; 04488 } else if (!strcasecmp(item, "active")) { 04489 mode = QMC_ACTIVE; 04490 } else if (!strcasecmp(item, "free")) { 04491 mode = QMC_FREE; 04492 } else if (!strcasecmp(item, "all")) { 04493 mode = QMC_ALL; 04494 } 04495 04496 if ((q = load_realtime_queue(name))) { 04497 ao2_lock(q); 04498 mem_iter = ao2_iterator_init(q->members, 0); 04499 while ((m = ao2_iterator_next(&mem_iter))) { 04500 switch (mode) { 04501 case QMC_VALID: 04502 /* Count the queue members who are logged in and presently answering calls */ 04503 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 04504 count++; 04505 } 04506 break; 04507 case QMC_PAUSED: 04508 /* Count paused members */ 04509 if (m->paused) { 04510 count++; 04511 } 04512 break; 04513 case QMC_ACTIVE: 04514 /* Count not paused members who are logged in and presently answering calls */ 04515 if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 04516 count++; 04517 } 04518 break; 04519 case QMC_FREE: 04520 /* Count free members in the queue */ 04521 if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) { 04522 count++; 04523 } 04524 break; 04525 default: 04526 count++; 04527 break; 04528 } 04529 ao2_ref(m, -1); 04530 } 04531 ao2_iterator_destroy(&mem_iter); 04532 ao2_unlock(q); 04533 } else 04534 ast_log(LOG_WARNING, "queue %s was not found\n", name); 04535 04536 snprintf(buf, len, "%d", count); 04537 ast_module_user_remove(lu); 04538 04539 return 0; 04540 }
static int queue_function_queuememberlist | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4585 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), member::interface, LOG_ERROR, LOG_WARNING, call_queue::members, and call_queue::name.
04586 { 04587 struct ast_module_user *u; 04588 struct call_queue *q; 04589 struct member *m; 04590 04591 /* Ensure an otherwise empty list doesn't return garbage */ 04592 buf[0] = '\0'; 04593 04594 if (ast_strlen_zero(data)) { 04595 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 04596 return -1; 04597 } 04598 04599 u = ast_module_user_add(chan); 04600 04601 AST_LIST_LOCK(&queues); 04602 AST_LIST_TRAVERSE(&queues, q, list) { 04603 if (!strcasecmp(q->name, data)) { 04604 ao2_lock(q); 04605 break; 04606 } 04607 } 04608 AST_LIST_UNLOCK(&queues); 04609 04610 if (q) { 04611 int buflen = 0, count = 0; 04612 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 04613 04614 while ((m = ao2_iterator_next(&mem_iter))) { 04615 /* strcat() is always faster than printf() */ 04616 if (count++) { 04617 strncat(buf + buflen, ",", len - buflen - 1); 04618 buflen++; 04619 } 04620 strncat(buf + buflen, m->interface, len - buflen - 1); 04621 buflen += strlen(m->interface); 04622 /* Safeguard against overflow (negative length) */ 04623 if (buflen >= len - 2) { 04624 ao2_ref(m, -1); 04625 ast_log(LOG_WARNING, "Truncating list\n"); 04626 break; 04627 } 04628 ao2_ref(m, -1); 04629 } 04630 ao2_iterator_destroy(&mem_iter); 04631 ao2_unlock(q); 04632 } else 04633 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04634 04635 /* We should already be terminated, but let's make sure. */ 04636 buf[len - 1] = '\0'; 04637 ast_module_user_remove(u); 04638 04639 return 0; 04640 }
static int queue_function_queuememberpaused | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4698 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), member_interface::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, and member::paused.
04699 { 04700 struct ast_module_user *lu; 04701 struct call_queue *q; 04702 struct member *cur; 04703 struct ao2_iterator mem_iter; 04704 char tmp[128] = ""; 04705 char *buffer; 04706 04707 AST_DECLARE_APP_ARGS(args, 04708 AST_APP_ARG(queue); 04709 AST_APP_ARG(interface); 04710 ); 04711 04712 AST_STANDARD_APP_ARGS(args, data); 04713 if (ast_strlen_zero(args.interface)) { 04714 ast_log(LOG_WARNING, "This function requires an interface name.\n"); 04715 return -1; 04716 } 04717 lu = ast_module_user_add(chan); 04718 04719 buffer = ast_malloc(len); 04720 buffer[0]='\0'; 04721 04722 AST_LIST_LOCK(&queues); 04723 AST_LIST_TRAVERSE(&queues, q, list) { 04724 ao2_lock(q); 04725 if (ast_strlen_zero(args.queue) || 04726 (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue))) 04727 ) { 04728 /* Iterate over queue members */ 04729 mem_iter = ao2_iterator_init(q->members, 0); 04730 while ((cur = ao2_iterator_next(&mem_iter))) { 04731 if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) { 04732 if (!ast_strlen_zero(args.queue)) { 04733 ast_copy_string(buffer, cur->paused?"1":"0", len); 04734 } else { 04735 snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, cur->paused?"1":"0"); 04736 strncat(buffer, tmp, sizeof(tmp)); 04737 } 04738 ao2_ref(cur, -1); 04739 break; 04740 } 04741 ao2_ref(cur, -1); 04742 } 04743 } 04744 ao2_unlock(q); 04745 } 04746 AST_LIST_UNLOCK(&queues); 04747 ast_copy_string(buf, buffer, len); 04748 ast_free(buffer); 04749 04750 ast_module_user_remove(lu); 04751 return 0; 04752 }
static int queue_function_queuememberstatus | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4642 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), devstate2str(), member_interface::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, and member::status.
04643 { 04644 struct ast_module_user *lu; 04645 struct call_queue *q; 04646 struct member *cur; 04647 struct ao2_iterator mem_iter; 04648 char tmp[128] = ""; 04649 char *buffer; 04650 04651 AST_DECLARE_APP_ARGS(args, 04652 AST_APP_ARG(queue); 04653 AST_APP_ARG(interface); 04654 ); 04655 04656 AST_STANDARD_APP_ARGS(args, data); 04657 if (ast_strlen_zero(args.interface)) { 04658 ast_log(LOG_WARNING, "This function requires an interface name.\n"); 04659 return -1; 04660 } 04661 lu = ast_module_user_add(chan); 04662 04663 buffer = ast_malloc(len); 04664 buffer[0]='\0'; 04665 04666 AST_LIST_LOCK(&queues); 04667 AST_LIST_TRAVERSE(&queues, q, list) { 04668 ao2_lock(q); 04669 if (ast_strlen_zero(args.queue) || 04670 (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue))) 04671 ) { 04672 /* Iterate over queue members */ 04673 mem_iter = ao2_iterator_init(q->members, 0); 04674 while ((cur = ao2_iterator_next(&mem_iter))) { 04675 if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) { 04676 if (!ast_strlen_zero(args.queue)) { 04677 ast_copy_string(buffer, devstate2str(cur->status), len); 04678 } else { 04679 snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, devstate2str(cur->status)); 04680 strncat(buffer, tmp, sizeof(tmp)); 04681 } 04682 ao2_ref(cur, -1); 04683 continue; 04684 } 04685 ao2_ref(cur, -1); 04686 } 04687 } 04688 ao2_unlock(q); 04689 } 04690 AST_LIST_UNLOCK(&queues); 04691 ast_copy_string(buf, buffer, len); 04692 ast_free(buffer); 04693 04694 ast_module_user_remove(lu); 04695 return 0; 04696 }
static int queue_function_queuewaitingcount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4542 of file app_queue.c.
References ao2_lock(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), ast_variables_destroy(), call_queue::count, LOG_ERROR, LOG_WARNING, call_queue::name, and var.
04543 { 04544 int count = 0; 04545 struct call_queue *q; 04546 struct ast_module_user *lu; 04547 struct ast_variable *var = NULL; 04548 04549 buf[0] = '\0'; 04550 04551 if (ast_strlen_zero(data)) { 04552 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04553 return -1; 04554 } 04555 04556 lu = ast_module_user_add(chan); 04557 04558 AST_LIST_LOCK(&queues); 04559 AST_LIST_TRAVERSE(&queues, q, list) { 04560 if (!strcasecmp(q->name, data)) { 04561 ao2_lock(q); 04562 break; 04563 } 04564 } 04565 AST_LIST_UNLOCK(&queues); 04566 04567 if (q) { 04568 count = q->count; 04569 ao2_unlock(q); 04570 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) { 04571 /* if the queue is realtime but was not found in memory, this 04572 * means that the queue had been deleted from memory since it was 04573 * "dead." This means it has a 0 waiting count 04574 */ 04575 count = 0; 04576 ast_variables_destroy(var); 04577 } else 04578 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04579 04580 snprintf(buf, len, "%d", count); 04581 ast_module_user_remove(lu); 04582 return 0; 04583 }
static int queue_member_count | ( | const char * | qname, | |
struct member_count * | qmc | |||
) | [static] |
Definition at line 5640 of file app_queue.c.
References member_count::active, member_count::all, ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_log(), member_count::free, member_count::inuse, load_realtime_queue(), LOG_WARNING, call_queue::members, member_count::paused, member::paused, member::status, and member_count::valid.
Referenced by qmc_handler().
05641 { 05642 int res = 0; 05643 struct call_queue *q; 05644 struct member *m; 05645 struct ao2_iterator mem_iter; 05646 05647 if ((q = load_realtime_queue(qname))) { 05648 ao2_lock(q); 05649 mem_iter = ao2_iterator_init(q->members, 0); 05650 while ((m = ao2_iterator_next(&mem_iter))) { 05651 /* Count the queue members in use */ 05652 if (m->status == AST_DEVICE_INUSE) { 05653 qmc->inuse++; 05654 } 05655 /* Count the queue members who are logged in and presently answering calls */ 05656 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 05657 qmc->valid++; 05658 } 05659 /* Count paused members */ 05660 if (m->paused) { 05661 qmc->paused++; 05662 } 05663 /* Count not paused members who are logged in and presently answering calls */ 05664 if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 05665 qmc->active++; 05666 } 05667 /* Count free members in the queue */ 05668 if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) { 05669 qmc->free++; 05670 } 05671 qmc->all++; 05672 ao2_ref(m, -1); 05673 } 05674 ao2_unlock(q); 05675 } else { 05676 ast_log(LOG_WARNING, "Queue %s was not found\n", qname); 05677 res = -1; 05678 } 05679 return res; 05680 }
static void queue_set_param | ( | struct call_queue * | q, | |
const char * | param, | |||
const char * | val, | |||
int | linenum, | |||
int | failunknown | |||
) | [static] |
Configure a queue parameter.
Definition at line 1034 of file app_queue.c.
References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_copy_string(), ast_log(), ast_strdupa, ast_true(), call_queue::autofill, call_queue::autopause, call_queue::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::moh, call_queue::monfmt, call_queue::monjoin, monjoin_dep_warning(), call_queue::montype, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_RINGALL, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_queues().
01035 { 01036 if (!strcasecmp(param, "musicclass") || 01037 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 01038 ast_copy_string(q->moh, val, sizeof(q->moh)); 01039 } else if (!strcasecmp(param, "announce")) { 01040 ast_copy_string(q->announce, val, sizeof(q->announce)); 01041 } else if (!strcasecmp(param, "context")) { 01042 ast_copy_string(q->context, val, sizeof(q->context)); 01043 } else if (!strcasecmp(param, "timeout")) { 01044 q->timeout = atoi(val); 01045 if (q->timeout < 0) 01046 q->timeout = DEFAULT_TIMEOUT; 01047 } else if (!strcasecmp(param, "ringinuse")) { 01048 q->ringinuse = ast_true(val); 01049 } else if (!strcasecmp(param, "setinterfacevar")) { 01050 q->setinterfacevar = ast_true(val); 01051 } else if (!strcasecmp(param, "monitor-join")) { 01052 monjoin_dep_warning(); 01053 q->monjoin = ast_true(val); 01054 } else if (!strcasecmp(param, "monitor-format")) { 01055 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 01056 } else if (!strcasecmp(param, "queue-youarenext")) { 01057 ast_copy_string(q->sound_next, val, sizeof(q->sound_next)); 01058 } else if (!strcasecmp(param, "queue-thereare")) { 01059 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare)); 01060 } else if (!strcasecmp(param, "queue-callswaiting")) { 01061 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls)); 01062 } else if (!strcasecmp(param, "queue-holdtime")) { 01063 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime)); 01064 } else if (!strcasecmp(param, "queue-minutes")) { 01065 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes)); 01066 } else if (!strcasecmp(param, "queue-seconds")) { 01067 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds)); 01068 } else if (!strcasecmp(param, "queue-lessthan")) { 01069 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan)); 01070 } else if (!strcasecmp(param, "queue-thankyou")) { 01071 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks)); 01072 } else if (!strcasecmp(param, "queue-reporthold")) { 01073 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold)); 01074 } else if (!strcasecmp(param, "announce-frequency")) { 01075 q->announcefrequency = atoi(val); 01076 } else if (!strcasecmp(param, "announce-round-seconds")) { 01077 q->roundingseconds = atoi(val); 01078 if (q->roundingseconds>60 || q->roundingseconds<0) { 01079 if (linenum >= 0) { 01080 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01081 "using 0 instead for queue '%s' at line %d of queues.conf\n", 01082 val, param, q->name, linenum); 01083 } else { 01084 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01085 "using 0 instead for queue '%s'\n", val, param, q->name); 01086 } 01087 q->roundingseconds=0; 01088 } 01089 } else if (!strcasecmp(param, "announce-holdtime")) { 01090 if (!strcasecmp(val, "once")) 01091 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 01092 else if (ast_true(val)) 01093 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 01094 else 01095 q->announceholdtime = 0; 01096 } else if (!strcasecmp(param, "periodic-announce")) { 01097 if (strchr(val, '|')) { 01098 char *s, *buf = ast_strdupa(val); 01099 unsigned int i = 0; 01100 01101 while ((s = strsep(&buf, "|"))) { 01102 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i])); 01103 i++; 01104 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 01105 break; 01106 } 01107 } else { 01108 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0])); 01109 } 01110 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 01111 q->periodicannouncefrequency = atoi(val); 01112 } else if (!strcasecmp(param, "retry")) { 01113 q->retry = atoi(val); 01114 if (q->retry <= 0) 01115 q->retry = DEFAULT_RETRY; 01116 } else if (!strcasecmp(param, "wrapuptime")) { 01117 q->wrapuptime = atoi(val); 01118 } else if (!strcasecmp(param, "autofill")) { 01119 q->autofill = ast_true(val); 01120 } else if (!strcasecmp(param, "monitor-type")) { 01121 if (!strcasecmp(val, "mixmonitor")) 01122 q->montype = 1; 01123 } else if (!strcasecmp(param, "autopause")) { 01124 q->autopause = ast_true(val); 01125 } else if (!strcasecmp(param, "maxlen")) { 01126 q->maxlen = atoi(val); 01127 if (q->maxlen < 0) 01128 q->maxlen = 0; 01129 } else if (!strcasecmp(param, "ringlimit")) { 01130 q->ringlimit = atoi(val); 01131 if (q->ringlimit < 0) 01132 q->ringlimit = 0; 01133 } else if (!strcasecmp(param, "servicelevel")) { 01134 q->servicelevel= atoi(val); 01135 } else if (!strcasecmp(param, "strategy")) { 01136 q->strategy = strat2int(val); 01137 if (q->strategy < 0) { 01138 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01139 val, q->name); 01140 q->strategy = QUEUE_STRATEGY_RINGALL; 01141 } 01142 } else if (!strcasecmp(param, "joinempty")) { 01143 if (!strcasecmp(val, "strict")) 01144 q->joinempty = QUEUE_EMPTY_STRICT; 01145 else if (ast_true(val)) 01146 q->joinempty = QUEUE_EMPTY_NORMAL; 01147 else 01148 q->joinempty = 0; 01149 } else if (!strcasecmp(param, "leavewhenempty")) { 01150 if (!strcasecmp(val, "strict")) 01151 q->leavewhenempty = QUEUE_EMPTY_STRICT; 01152 else if (ast_true(val)) 01153 q->leavewhenempty = QUEUE_EMPTY_NORMAL; 01154 else 01155 q->leavewhenempty = 0; 01156 } else if (!strcasecmp(param, "eventmemberstatus")) { 01157 q->maskmemberstatus = !ast_true(val); 01158 } else if (!strcasecmp(param, "eventwhencalled")) { 01159 if (!strcasecmp(val, "vars")) { 01160 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 01161 } else { 01162 q->eventwhencalled = ast_true(val) ? 1 : 0; 01163 } 01164 } else if (!strcasecmp(param, "reportholdtime")) { 01165 q->reportholdtime = ast_true(val); 01166 } else if (!strcasecmp(param, "memberdelay")) { 01167 q->memberdelay = atoi(val); 01168 } else if (!strcasecmp(param, "weight")) { 01169 q->weight = atoi(val); 01170 if (q->weight) 01171 use_weight++; 01172 /* With Realtime queues, if the last queue using weights is deleted in realtime, 01173 we will not see any effect on use_weight until next reload. */ 01174 } else if (!strcasecmp(param, "timeoutrestart")) { 01175 q->timeoutrestart = ast_true(val); 01176 } else if (failunknown) { 01177 if (linenum >= 0) { 01178 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 01179 q->name, param, linenum); 01180 } else { 01181 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 01182 } 01183 } 01184 }
static int queue_show | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 5212 of file app_queue.c.
References __queues_show().
Referenced by __queues_show().
05213 { 05214 return __queues_show(NULL, 0, fd, argc, argv); 05215 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 2824 of file app_queue.c.
References ast_free.
02825 { 02826 struct queue_transfer_ds *qtds = data; 02827 ast_free(qtds); 02828 }
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 2847 of file app_queue.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, and update_queue().
02848 { 02849 struct queue_transfer_ds *qtds = data; 02850 struct queue_ent *qe = qtds->qe; 02851 struct member *member = qtds->member; 02852 time_t callstart = qtds->starttime; 02853 int callcompletedinsl = qtds->callcompletedinsl; 02854 struct ast_datastore *datastore; 02855 02856 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 02857 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 02858 (long) (time(NULL) - callstart)); 02859 02860 update_queue(qe->parent, member, callcompletedinsl); 02861 02862 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 02863 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 02864 ast_channel_datastore_remove(old_chan, datastore); 02865 } else { 02866 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 02867 } 02868 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 1750 of file app_queue.c.
References ao2_lock(), ao2_unlock(), call_queue::holdtime, and queue_ent::parent.
Referenced by try_calling().
01751 { 01752 int oldvalue; 01753 01754 /* Calculate holdtime using an exponential average */ 01755 /* Thanks to SRT for this contribution */ 01756 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 01757 01758 ao2_lock(qe->parent); 01759 oldvalue = qe->parent->holdtime; 01760 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 01761 ao2_unlock(qe->parent); 01762 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 2247 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, and queue_ent::start.
Referenced by queue_exec(), and try_calling().
02248 { 02249 ao2_lock(qe->parent); 02250 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 02251 "Queue: %s\r\n" 02252 "Uniqueid: %s\r\n" 02253 "Position: %d\r\n" 02254 "OriginalPosition: %d\r\n" 02255 "HoldTime: %d\r\n", 02256 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 02257 02258 qe->parent->callsabandoned++; 02259 ao2_unlock(qe->parent); 02260 }
static int reload | ( | void | ) | [static] |
Definition at line 5884 of file app_queue.c.
References reload_queues().
05885 { 05886 reload_queues(); 05887 return 0; 05888 }
static void reload_queue_members | ( | void | ) | [static] |
Definition at line 3772 of file app_queue.c.
References add_to_queue(), ao2_lock(), ao2_unlock(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strlen_zero(), ERANGE, errno, member_interface::interface, ast_db_entry::key, load_realtime_queue(), LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, option_debug, member::paused, member::penalty, PM_MAX_LEN, RES_OUTOFMEMORY, member::state_interface, and strsep().
Referenced by load_module().
03773 { 03774 char *cur_ptr; 03775 char *queue_name; 03776 char *member; 03777 char *interface; 03778 char *membername = NULL; 03779 char *state_interface; 03780 char *penalty_tok; 03781 int penalty = 0; 03782 char *paused_tok; 03783 int paused = 0; 03784 struct ast_db_entry *db_tree; 03785 struct ast_db_entry *entry; 03786 struct call_queue *cur_queue; 03787 char queue_data[PM_MAX_LEN]; 03788 03789 AST_LIST_LOCK(&queues); 03790 03791 /* Each key in 'pm_family' is the name of a queue */ 03792 db_tree = ast_db_gettree(pm_family, NULL); 03793 for (entry = db_tree; entry; entry = entry->next) { 03794 03795 queue_name = entry->key + strlen(pm_family) + 2; 03796 03797 AST_LIST_TRAVERSE(&queues, cur_queue, list) { 03798 ao2_lock(cur_queue); 03799 if (!strcmp(queue_name, cur_queue->name)) 03800 break; 03801 ao2_unlock(cur_queue); 03802 } 03803 03804 if (!cur_queue) 03805 cur_queue = load_realtime_queue(queue_name); 03806 03807 if (!cur_queue) { 03808 /* If the queue no longer exists, remove it from the 03809 * database */ 03810 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 03811 ast_db_del(pm_family, queue_name); 03812 continue; 03813 } else 03814 ao2_unlock(cur_queue); 03815 03816 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) 03817 continue; 03818 03819 cur_ptr = queue_data; 03820 while ((member = strsep(&cur_ptr, "|"))) { 03821 if (ast_strlen_zero(member)) 03822 continue; 03823 03824 interface = strsep(&member, ";"); 03825 penalty_tok = strsep(&member, ";"); 03826 paused_tok = strsep(&member, ";"); 03827 membername = strsep(&member, ";"); 03828 state_interface = strsep(&member,";"); 03829 03830 if (!penalty_tok) { 03831 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 03832 break; 03833 } 03834 penalty = strtol(penalty_tok, NULL, 10); 03835 if (errno == ERANGE) { 03836 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 03837 break; 03838 } 03839 03840 if (!paused_tok) { 03841 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 03842 break; 03843 } 03844 paused = strtol(paused_tok, NULL, 10); 03845 if ((errno == ERANGE) || paused < 0 || paused > 1) { 03846 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 03847 break; 03848 } 03849 if (ast_strlen_zero(membername)) 03850 membername = interface; 03851 03852 if (option_debug) 03853 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 03854 03855 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) { 03856 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 03857 break; 03858 } 03859 } 03860 } 03861 03862 AST_LIST_UNLOCK(&queues); 03863 if (db_tree) { 03864 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 03865 ast_db_freetree(db_tree); 03866 } 03867 }
static int reload_queues | ( | void | ) | [static] |
Definition at line 4839 of file app_queue.c.
References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), AST_APP_ARG, ast_category_browse(), ast_config_destroy(), ast_config_load(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), AST_NONSTANDARD_APP_ARGS, ast_skip_blanks(), ast_strdup, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), create_queue_member(), call_queue::dead, member::delme, member::dynamic, call_queue::found, init_queue(), member::interface, member_interface::interface, ast_variable::lineno, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, ast_variable::name, call_queue::name, ast_variable::next, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::state_interface, member::status, strat2int(), call_queue::strategy, ast_variable::value, and var.
Referenced by load_module(), and reload().
04840 { 04841 struct call_queue *q; 04842 struct ast_config *cfg; 04843 char *cat, *tmp; 04844 struct ast_variable *var; 04845 struct member *cur, *newm; 04846 struct ao2_iterator mem_iter; 04847 int new; 04848 const char *general_val = NULL; 04849 char *parse; 04850 char *interface, *state_interface; 04851 char *membername = NULL; 04852 int penalty; 04853 AST_DECLARE_APP_ARGS(args, 04854 AST_APP_ARG(interface); 04855 AST_APP_ARG(penalty); 04856 AST_APP_ARG(membername); 04857 AST_APP_ARG(state_interface); 04858 ); 04859 04860 if (!(cfg = ast_config_load("queues.conf"))) { 04861 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 04862 return 0; 04863 } 04864 AST_LIST_LOCK(&queues); 04865 use_weight=0; 04866 /* Mark all non-realtime queues as dead for the moment */ 04867 AST_LIST_TRAVERSE(&queues, q, list) { 04868 if (!q->realtime) { 04869 q->dead = 1; 04870 q->found = 0; 04871 } 04872 } 04873 04874 /* Chug through config file */ 04875 cat = NULL; 04876 while ((cat = ast_category_browse(cfg, cat)) ) { 04877 if (!strcasecmp(cat, "general")) { 04878 /* Initialize global settings */ 04879 queue_debug = 0; 04880 if ((general_val = ast_variable_retrieve(cfg, "general", "debug"))) 04881 queue_debug = ast_true(general_val); 04882 queue_persistent_members = 0; 04883 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 04884 queue_persistent_members = ast_true(general_val); 04885 autofill_default = 0; 04886 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 04887 autofill_default = ast_true(general_val); 04888 montype_default = 0; 04889 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) 04890 if (!strcasecmp(general_val, "mixmonitor")) 04891 montype_default = 1; 04892 shared_lastcall = 0; 04893 if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall"))) 04894 shared_lastcall = ast_true(general_val); 04895 } else { /* Define queue */ 04896 /* Look for an existing one */ 04897 AST_LIST_TRAVERSE(&queues, q, list) { 04898 if (!strcmp(q->name, cat)) 04899 break; 04900 } 04901 if (!q) { 04902 /* Make one then */ 04903 if (!(q = alloc_queue(cat))) { 04904 /* TODO: Handle memory allocation failure */ 04905 } 04906 new = 1; 04907 } else 04908 new = 0; 04909 if (q) { 04910 const char *tmpvar; 04911 if (!new) 04912 ao2_lock(q); 04913 /* Check if a queue with this name already exists */ 04914 if (q->found) { 04915 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat); 04916 if (!new) 04917 ao2_unlock(q); 04918 continue; 04919 } 04920 04921 /* Due to the fact that the "rrordered" strategy will have a different allocation 04922 * scheme for queue members, we must devise the queue's strategy before other initializations. 04923 * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2 04924 * container used will have only a single bucket instead of the typical number. 04925 */ 04926 if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) { 04927 q->strategy = strat2int(tmpvar); 04928 if (q->strategy < 0) { 04929 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", tmpvar, q->name); 04930 q->strategy = QUEUE_STRATEGY_RINGALL; 04931 } 04932 } else { 04933 q->strategy = QUEUE_STRATEGY_RINGALL; 04934 } 04935 04936 /* Re-initialize the queue, and clear statistics */ 04937 init_queue(q); 04938 clear_queue(q); 04939 mem_iter = ao2_iterator_init(q->members, 0); 04940 while ((cur = ao2_iterator_next(&mem_iter))) { 04941 if (!cur->dynamic) { 04942 cur->delme = 1; 04943 } 04944 ao2_ref(cur, -1); 04945 } 04946 ao2_iterator_destroy(&mem_iter); 04947 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 04948 if (!strcasecmp(var->name, "member")) { 04949 struct member tmpmem; 04950 membername = NULL; 04951 04952 if (ast_strlen_zero(var->value)) { 04953 ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno); 04954 continue; 04955 } 04956 04957 /* Add a new member */ 04958 if (!(parse = ast_strdup(var->value))) { 04959 continue; 04960 } 04961 04962 AST_NONSTANDARD_APP_ARGS(args, parse, ','); 04963 04964 interface = args.interface; 04965 if (!ast_strlen_zero(args.penalty)) { 04966 tmp = ast_skip_blanks(args.penalty); 04967 penalty = atoi(tmp); 04968 if (penalty < 0) { 04969 penalty = 0; 04970 } 04971 } else 04972 penalty = 0; 04973 04974 if (!ast_strlen_zero(args.membername)) { 04975 membername = ast_skip_blanks(args.membername); 04976 } 04977 04978 if (!ast_strlen_zero(args.state_interface)) { 04979 state_interface = ast_skip_blanks(args.state_interface); 04980 } else { 04981 state_interface = interface; 04982 } 04983 04984 /* Find the old position in the list */ 04985 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04986 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 04987 04988 /* Only attempt removing from interfaces list if the new state_interface is different than the old one */ 04989 if (cur && strcasecmp(cur->state_interface, state_interface)) { 04990 remove_from_interfaces(cur->state_interface); 04991 } 04992 04993 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface); 04994 if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) { 04995 add_to_interfaces(state_interface); 04996 } 04997 ao2_link(q->members, newm); 04998 ao2_ref(newm, -1); 04999 newm = NULL; 05000 05001 if (cur) 05002 ao2_ref(cur, -1); 05003 else { 05004 q->membercount++; 05005 } 05006 ast_free(parse); 05007 } else { 05008 queue_set_param(q, var->name, var->value, var->lineno, 1); 05009 } 05010 } 05011 05012 /* Free remaining members marked as delme */ 05013 mem_iter = ao2_iterator_init(q->members, 0); 05014 while ((cur = ao2_iterator_next(&mem_iter))) { 05015 if (! cur->delme) { 05016 ao2_ref(cur, -1); 05017 continue; 05018 } 05019 05020 q->membercount--; 05021 ao2_unlink(q->members, cur); 05022 remove_from_interfaces(cur->state_interface); 05023 ao2_ref(cur, -1); 05024 } 05025 ao2_iterator_destroy(&mem_iter); 05026 05027 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 05028 rr_dep_warning(); 05029 05030 if (new) { 05031 AST_LIST_INSERT_HEAD(&queues, q, list); 05032 } else 05033 ao2_unlock(q); 05034 } 05035 } 05036 } 05037 ast_config_destroy(cfg); 05038 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) { 05039 if (q->dead) { 05040 AST_LIST_REMOVE_CURRENT(&queues, list); 05041 ao2_ref(q, -1); 05042 } else { 05043 ao2_lock(q); 05044 mem_iter = ao2_iterator_init(q->members, 0); 05045 while ((cur = ao2_iterator_next(&mem_iter))) { 05046 if (cur->dynamic) 05047 q->membercount++; 05048 cur->status = ast_device_state(cur->state_interface); 05049 ao2_ref(cur, -1); 05050 } 05051 ao2_iterator_destroy(&mem_iter); 05052 ao2_unlock(q); 05053 } 05054 } 05055 AST_LIST_TRAVERSE_SAFE_END; 05056 AST_LIST_UNLOCK(&queues); 05057 return 1; 05058 }
static int remove_from_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 994 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), free, member_interface::interface, interface_exists_global(), LOG_DEBUG, and option_debug.
Referenced by find_queue_by_name_rt(), free_members(), reload_queues(), remove_from_queue(), rt_handle_member_record(), and update_realtime_members().
00995 { 00996 struct member_interface *curint; 00997 00998 if (interface_exists_global(interface)) 00999 return 0; 01000 01001 AST_LIST_LOCK(&interfaces); 01002 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) { 01003 if (!strcasecmp(curint->interface, interface)) { 01004 if (option_debug) 01005 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface); 01006 AST_LIST_REMOVE_CURRENT(&interfaces, list); 01007 free(curint); 01008 break; 01009 } 01010 } 01011 AST_LIST_TRAVERSE_SAFE_END; 01012 AST_LIST_UNLOCK(&interfaces); 01013 01014 return 0; 01015 }
static int remove_from_queue | ( | const char * | queuename, | |
const char * | interface | |||
) | [static] |
Definition at line 3620 of file app_queue.c.
References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event(), call_queue::membercount, member::membername, call_queue::members, call_queue::name, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::state_interface.
Referenced by handle_queue_remove_member(), manager_remove_queue_member(), and rqm_exec().
03621 { 03622 struct call_queue *q; 03623 struct member *mem, tmpmem; 03624 int res = RES_NOSUCHQUEUE; 03625 03626 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 03627 03628 AST_LIST_LOCK(&queues); 03629 AST_LIST_TRAVERSE(&queues, q, list) { 03630 ao2_lock(q); 03631 if (strcmp(q->name, queuename)) { 03632 ao2_unlock(q); 03633 continue; 03634 } 03635 03636 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 03637 /* XXX future changes should beware of this assumption!! */ 03638 if (!mem->dynamic) { 03639 res = RES_NOT_DYNAMIC; 03640 ao2_ref(mem, -1); 03641 ao2_unlock(q); 03642 break; 03643 } 03644 q->membercount--; 03645 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 03646 "Queue: %s\r\n" 03647 "Location: %s\r\n" 03648 "MemberName: %s\r\n", 03649 q->name, mem->interface, mem->membername); 03650 ao2_unlink(q->members, mem); 03651 remove_from_interfaces(mem->state_interface); 03652 ao2_ref(mem, -1); 03653 03654 if (queue_persistent_members) 03655 dump_queue_members(q); 03656 03657 res = RES_OKAY; 03658 } else { 03659 res = RES_EXISTS; 03660 } 03661 ao2_unlock(q); 03662 break; 03663 } 03664 03665 AST_LIST_UNLOCK(&queues); 03666 03667 return res; 03668 }
static void remove_queue | ( | struct call_queue * | q | ) | [static] |
removes a call_queue from the list of call_queues
Definition at line 534 of file app_queue.c.
References ao2_ref(), AST_LIST_LOCK, AST_LIST_REMOVE, and AST_LIST_UNLOCK.
Referenced by find_queue_by_name_rt(), and leave_queue().
00535 { 00536 AST_LIST_LOCK(&queues); 00537 if (AST_LIST_REMOVE(&queues, q, list)) { 00538 ao2_ref(q, -1); 00539 } 00540 AST_LIST_UNLOCK(&queues); 00541 }
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
Definition at line 1953 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_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, ast_channel::dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, callattempt::lastqueue, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, callattempt::stillgoing, update_status(), ast_cdr::userfield, vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.
Referenced by ring_one().
01954 { 01955 int res; 01956 int status; 01957 char tech[256]; 01958 char *location, *location2; 01959 const char *macrocontext, *macroexten; 01960 char pickupmark[256], chan[128]; 01961 01962 /* on entry here, we know that tmp->chan == NULL */ 01963 if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) || 01964 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) { 01965 if (queue_debug) 01966 ast_log(LOG_NOTICE, "Wrapuptime not yet expired on queue %s for %s\n", 01967 (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface); 01968 tmp->stillgoing = 0; 01969 (*busies)++; 01970 return 0; 01971 } 01972 01973 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 01974 if (queue_debug) 01975 ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface); 01976 tmp->stillgoing = 0; 01977 (*busies)++; 01978 return 0; 01979 } 01980 01981 if (tmp->member->paused) { 01982 if (queue_debug) 01983 ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface); 01984 tmp->stillgoing = 0; 01985 (*busies)++; 01986 return 0; 01987 } 01988 if (use_weight && compare_weight(qe->parent,tmp->member)) { 01989 if (queue_debug) 01990 ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 01991 if (qe->chan->cdr) 01992 ast_cdr_busy(qe->chan->cdr); 01993 tmp->stillgoing = 0; 01994 (*busies)++; 01995 return 0; 01996 } 01997 01998 ast_copy_string(tech, tmp->interface, sizeof(tech)); 01999 if ((location = strchr(tech, '/'))) 02000 *location++ = '\0'; 02001 else 02002 location = ""; 02003 02004 /* Request the peer */ 02005 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); 02006 if (!tmp->chan) { /* If we can't, just go on to the next call */ 02007 if (queue_debug) 02008 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech); 02009 if (qe->chan->cdr) 02010 ast_cdr_busy(qe->chan->cdr); 02011 tmp->stillgoing = 0; 02012 02013 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface)); 02014 02015 ao2_lock(qe->parent); 02016 qe->parent->rrpos++; 02017 ao2_unlock(qe->parent); 02018 02019 (*busies)++; 02020 return 0; 02021 } 02022 02023 /* Increment ring count */ 02024 tmp->member->ringcount++; 02025 tmp->chan->appl = "AppQueue"; 02026 tmp->chan->data = "(Outgoing Line)"; 02027 tmp->chan->whentohangup = 0; 02028 if (tmp->chan->cid.cid_num) 02029 free(tmp->chan->cid.cid_num); 02030 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); 02031 if (tmp->chan->cid.cid_name) 02032 free(tmp->chan->cid.cid_name); 02033 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); 02034 if (tmp->chan->cid.cid_ani) 02035 free(tmp->chan->cid.cid_ani); 02036 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); 02037 02038 /* Inherit specially named variables from parent channel */ 02039 ast_channel_inherit_variables(qe->chan, tmp->chan); 02040 ast_channel_datastore_inherit(qe->chan, tmp->chan); 02041 02042 /* Presense of ADSI CPE on outgoing channel follows ours */ 02043 tmp->chan->adsicpe = qe->chan->adsicpe; 02044 02045 /* Inherit context and extension */ 02046 ast_channel_lock(qe->chan); 02047 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 02048 if (!ast_strlen_zero(macrocontext)) 02049 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext)); 02050 else 02051 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext)); 02052 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 02053 if (!ast_strlen_zero(macroexten)) 02054 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 02055 else 02056 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 02057 if (ast_cdr_isset_unanswered()) { 02058 /* they want to see the unanswered dial attempts! */ 02059 /* set up the CDR fields on all the CDRs to give sensical information */ 02060 ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name); 02061 strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid); 02062 strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel); 02063 strcpy(tmp->chan->cdr->src, qe->chan->cdr->src); 02064 strcpy(tmp->chan->cdr->dst, qe->chan->exten); 02065 strcpy(tmp->chan->cdr->dcontext, qe->chan->context); 02066 strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp); 02067 strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata); 02068 tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags; 02069 strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode); 02070 strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield); 02071 } 02072 ast_channel_unlock(qe->chan); 02073 02074 /* Add a PICKUPMARK variable to ringing interface */ 02075 if (option_debug > 2) 02076 ast_log(LOG_DEBUG, "chan %s, tech: %s, part %s\n", tmp->chan->name, tech, location); 02077 /* Delete DAHDI ring pattern in tech like DAHDI/1r2 */ 02078 ast_copy_string(chan, location, sizeof(chan)); 02079 if (!strncasecmp(tech, "dahdi", 5)) { 02080 if((chan[0] > '0') && (chan[0] <= '9')) { 02081 if ((location2 = strchr(chan, 'r'))) 02082 *location2++ = '\0'; 02083 } 02084 } 02085 snprintf(pickupmark, sizeof(pickupmark), "%s/%s", tech, chan); 02086 pbx_builtin_setvar_helper(tmp->chan, "PICKUPMARK", pickupmark); 02087 02088 /* Place the call, but don't wait on the answer */ 02089 if ((res = ast_call(tmp->chan, location, 0))) { 02090 /* Again, keep going even if there's an error */ 02091 if (option_debug) 02092 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); 02093 if (option_verbose > 2) 02094 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface); 02095 do_hang(tmp); 02096 (*busies)++; 02097 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface)); 02098 return 0; 02099 } else if (qe->parent->eventwhencalled) { 02100 char vars[2048]; 02101 02102 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 02103 "AgentCalled: %s\r\n" 02104 "AgentName: %s\r\n" 02105 "ChannelCalling: %s\r\n" 02106 "CallerID: %s\r\n" 02107 "CallerIDName: %s\r\n" 02108 "Context: %s\r\n" 02109 "Extension: %s\r\n" 02110 "Priority: %d\r\n" 02111 "%s", 02112 tmp->interface, tmp->member->membername, qe->chan->name, 02113 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", 02114 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", 02115 qe->chan->context, qe->chan->exten, qe->chan->priority, 02116 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 02117 if (option_verbose > 2) 02118 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface); 02119 } 02120 02121 update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface)); 02122 return 1; 02123 }
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
Returns 1 if a member was called successfully, 0 otherwise
Definition at line 2149 of file app_queue.c.
References ast_log(), callattempt::chan, find_best(), callattempt::interface, LOG_DEBUG, callattempt::metric, option_debug, 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().
02150 { 02151 int ret = 0; 02152 02153 while (ret == 0) { 02154 struct callattempt *best = find_best(outgoing); 02155 if (!best) { 02156 if (option_debug) 02157 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); 02158 break; 02159 } 02160 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 02161 struct callattempt *cur; 02162 /* Ring everyone who shares this best metric (for ringall) */ 02163 for (cur = outgoing; cur; cur = cur->q_next) { 02164 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 02165 if (option_debug) 02166 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 02167 ret |= ring_entry(qe, cur, busies); 02168 } 02169 } 02170 } else { 02171 /* Ring just the best channel */ 02172 if (option_debug) 02173 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric); 02174 ret = ring_entry(qe, best, busies); 02175 } 02176 } 02177 02178 return ret; 02179 }
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 2263 of file app_queue.c.
References ast_indicate(), ast_moh_start(), ast_queue_log(), ast_verbose(), call_queue::autopause, queue_ent::chan, queue_ent::moh, call_queue::name, option_verbose, queue_ent::parent, queue_ent::ring_when_ringing, set_member_paused(), and VERBOSE_PREFIX_3.
Referenced by wait_for_answer().
02264 { 02265 if (option_verbose > 2) 02266 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime); 02267 02268 /* Stop ringing, and resume MOH if specified */ 02269 if (qe->ring_when_ringing) { 02270 ast_indicate(qe->chan, -1); 02271 ast_moh_start(qe->chan, qe->moh, NULL); 02272 } 02273 02274 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 02275 if (qe->parent->autopause && pause) { 02276 if (!set_member_paused(qe->parent->name, interface, 1)) { 02277 if (option_verbose > 2) 02278 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); 02279 } else { 02280 if (option_verbose > 2) 02281 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 02282 } 02283 } 02284 return; 02285 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3985 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.
Referenced by load_module().
03986 { 03987 int res=-1; 03988 struct ast_module_user *lu; 03989 char *parse, *temppos = NULL; 03990 int priority_jump = 0; 03991 AST_DECLARE_APP_ARGS(args, 03992 AST_APP_ARG(queuename); 03993 AST_APP_ARG(interface); 03994 AST_APP_ARG(options); 03995 ); 03996 03997 03998 if (ast_strlen_zero(data)) { 03999 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n"); 04000 return -1; 04001 } 04002 04003 parse = ast_strdupa(data); 04004 04005 AST_STANDARD_APP_ARGS(args, parse); 04006 04007 lu = ast_module_user_add(chan); 04008 04009 if (ast_strlen_zero(args.interface)) { 04010 args.interface = ast_strdupa(chan->name); 04011 temppos = strrchr(args.interface, '-'); 04012 if (temppos) 04013 *temppos = '\0'; 04014 } 04015 04016 if (args.options) { 04017 if (strchr(args.options, 'j')) 04018 priority_jump = 1; 04019 } 04020 04021 switch (remove_from_queue(args.queuename, args.interface)) { 04022 case RES_OKAY: 04023 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 04024 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 04025 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 04026 res = 0; 04027 break; 04028 case RES_EXISTS: 04029 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 04030 if (priority_jump || ast_opt_priority_jumping) 04031 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 04032 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 04033 res = 0; 04034 break; 04035 case RES_NOSUCHQUEUE: 04036 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 04037 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 04038 res = 0; 04039 break; 04040 case RES_NOT_DYNAMIC: 04041 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 04042 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 04043 res = 0; 04044 break; 04045 } 04046 04047 ast_module_user_remove(lu); 04048 04049 return res; 04050 }
static void rr_dep_warning | ( | void | ) | [static] |
Definition at line 476 of file app_queue.c.
References ast_log(), and LOG_NOTICE.
Referenced by find_queue_by_name_rt(), and reload_queues().
00477 { 00478 static unsigned int warned = 0; 00479 00480 if (!warned) { 00481 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n"); 00482 warned = 1; 00483 } 00484 }
static void rt_handle_member_record | ( | struct call_queue * | q, | |
char * | interface, | |||
const char * | membername, | |||
const char * | penalty_str, | |||
const char * | paused_str, | |||
const char * | state_interface | |||
) | [static] |
Definition at line 1186 of file app_queue.c.
References add_to_interfaces(), ao2_find(), ao2_ref(), ast_copy_string(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, member::paused, member::penalty, member::realtime, remove_from_interfaces(), and member::state_interface.
Referenced by find_queue_by_name_rt(), and update_realtime_members().
01187 { 01188 struct member *m, tmpmem; 01189 int penalty = 0; 01190 int paused = 0; 01191 01192 if (penalty_str) { 01193 penalty = atoi(penalty_str); 01194 if (penalty < 0) 01195 penalty = 0; 01196 } 01197 01198 if (paused_str) { 01199 paused = atoi(paused_str); 01200 if (paused < 0) 01201 paused = 0; 01202 } 01203 01204 /* Find the member, or the place to put a new one. */ 01205 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 01206 m = ao2_find(q->members, &tmpmem, OBJ_POINTER); 01207 01208 /* Create a new one if not found, else update penalty */ 01209 if (!m) { 01210 if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) { 01211 m->dead = 0; 01212 m->realtime = 1; 01213 add_to_interfaces(m->state_interface); 01214 ao2_link(q->members, m); 01215 ao2_ref(m, -1); 01216 m = NULL; 01217 q->membercount++; 01218 } 01219 } else { 01220 m->dead = 0; /* Do not delete this one. */ 01221 if (paused_str) 01222 m->paused = paused; 01223 if (strcasecmp(state_interface, m->state_interface)) { 01224 remove_from_interfaces(m->state_interface); 01225 ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface)); 01226 add_to_interfaces(m->state_interface); 01227 } 01228 m->penalty = penalty; 01229 ao2_ref(m, -1); 01230 } 01231 }
static int say_periodic_announcement | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 2205 of file app_queue.c.
References ast_moh_start(), ast_moh_stop(), ast_verbose(), queue_ent::chan, queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, MAX_PERIODIC_ANNOUNCEMENTS, queue_ent::moh, option_verbose, queue_ent::parent, call_queue::periodicannouncefrequency, play_file(), call_queue::sound_periodicannounce, valid_exit(), and VERBOSE_PREFIX_3.
Referenced by queue_exec(), and wait_our_turn().
02206 { 02207 int res = 0; 02208 time_t now; 02209 02210 /* Get the current time */ 02211 time(&now); 02212 02213 /* Check to see if it is time to announce */ 02214 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 02215 return 0; 02216 02217 /* Stop the music on hold so we can play our own file */ 02218 ast_moh_stop(qe->chan); 02219 02220 if (option_verbose > 2) 02221 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n"); 02222 02223 /* Check to make sure we have a sound file. If not, reset to the first sound file */ 02224 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) { 02225 qe->last_periodic_announce_sound = 0; 02226 } 02227 02228 /* play the announcement */ 02229 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]); 02230 02231 if (res > 0 && !valid_exit(qe, res)) 02232 res = 0; 02233 02234 /* Resume Music on Hold if the caller is going to stay in the queue */ 02235 if (!res) 02236 ast_moh_start(qe->chan, qe->moh, NULL); 02237 02238 /* update last_periodic_announce_time */ 02239 qe->last_periodic_announce_time = now; 02240 02241 /* Update the current periodic announcement to the next announcement */ 02242 qe->last_periodic_announce_sound++; 02243 02244 return res; 02245 }
static int say_position | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1641 of file app_queue.c.
References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, AST_DIGIT_ANY, ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verbose(), queue_ent::chan, call_queue::holdtime, queue_ent::last_pos, queue_ent::last_pos_said, queue_ent::moh, call_queue::name, option_verbose, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, valid_exit(), and VERBOSE_PREFIX_3.
Referenced by queue_exec(), and wait_our_turn().
01642 { 01643 int res = 0, avgholdmins, avgholdsecs; 01644 time_t now; 01645 01646 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ 01647 time(&now); 01648 if ((now - qe->last_pos) < 15) 01649 return 0; 01650 01651 /* If either our position has changed, or we are over the freq timer, say position */ 01652 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 01653 return 0; 01654 01655 ast_moh_stop(qe->chan); 01656 /* Say we're next, if we are */ 01657 if (qe->pos == 1) { 01658 res = play_file(qe->chan, qe->parent->sound_next); 01659 if (res) 01660 goto playout; 01661 else 01662 goto posout; 01663 } else { 01664 res = play_file(qe->chan, qe->parent->sound_thereare); 01665 if (res) 01666 goto playout; 01667 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */ 01668 if (res) 01669 goto playout; 01670 res = play_file(qe->chan, qe->parent->sound_calls); 01671 if (res) 01672 goto playout; 01673 } 01674 /* Round hold time to nearest minute */ 01675 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 01676 01677 /* If they have specified a rounding then round the seconds as well */ 01678 if (qe->parent->roundingseconds) { 01679 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 01680 avgholdsecs *= qe->parent->roundingseconds; 01681 } else { 01682 avgholdsecs = 0; 01683 } 01684 01685 if (option_verbose > 2) 01686 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 01687 01688 /* If the hold time is >1 min, if it's enabled, and if it's not 01689 supposed to be only once and we have already said it, say it */ 01690 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 01691 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 01692 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 01693 res = play_file(qe->chan, qe->parent->sound_holdtime); 01694 if (res) 01695 goto playout; 01696 01697 if (avgholdmins > 0) { 01698 if (avgholdmins < 2) { 01699 res = play_file(qe->chan, qe->parent->sound_lessthan); 01700 if (res) 01701 goto playout; 01702 01703 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL); 01704 if (res) 01705 goto playout; 01706 } else { 01707 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 01708 if (res) 01709 goto playout; 01710 } 01711 01712 res = play_file(qe->chan, qe->parent->sound_minutes); 01713 if (res) 01714 goto playout; 01715 } 01716 if (avgholdsecs>0) { 01717 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 01718 if (res) 01719 goto playout; 01720 01721 res = play_file(qe->chan, qe->parent->sound_seconds); 01722 if (res) 01723 goto playout; 01724 } 01725 01726 } 01727 01728 posout: 01729 if (option_verbose > 2) 01730 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", 01731 qe->chan->name, qe->parent->name, qe->pos); 01732 res = play_file(qe->chan, qe->parent->sound_thanks); 01733 01734 playout: 01735 01736 if ((res > 0 && !valid_exit(qe, res))) 01737 res = 0; 01738 01739 /* Set our last_pos indicators */ 01740 qe->last_pos = now; 01741 qe->last_pos_said = qe->pos; 01742 01743 /* Don't restart music on hold if we're about to exit the caller from the queue */ 01744 if (!res) 01745 ast_moh_start(qe->chan, qe->moh, NULL); 01746 01747 return res; 01748 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
int | paused | |||
) | [static] |
Definition at line 3726 of file app_queue.c.
References ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), LOG_DEBUG, manager_event(), member::membername, call_queue::name, member::paused, member::realtime, RESULT_FAILURE, RESULT_SUCCESS, and update_realtime_member_field().
Referenced by manager_pause_queue_member(), pqm_exec(), rna(), and upqm_exec().
03727 { 03728 int found = 0; 03729 struct call_queue *q; 03730 struct member *mem; 03731 03732 /* Special event for when all queues are paused - individual events still generated */ 03733 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 03734 if (ast_strlen_zero(queuename)) 03735 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 03736 03737 AST_LIST_LOCK(&queues); 03738 AST_LIST_TRAVERSE(&queues, q, list) { 03739 ao2_lock(q); 03740 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 03741 if ((mem = interface_exists(q, interface))) { 03742 found++; 03743 if (mem->paused == paused) 03744 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 03745 mem->paused = paused; 03746 03747 if (queue_persistent_members) 03748 dump_queue_members(q); 03749 03750 if (mem->realtime) 03751 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 03752 03753 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", ""); 03754 03755 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 03756 "Queue: %s\r\n" 03757 "Location: %s\r\n" 03758 "MemberName: %s\r\n" 03759 "Paused: %d\r\n", 03760 q->name, mem->interface, mem->membername, paused); 03761 ao2_ref(mem, -1); 03762 } 03763 } 03764 ao2_unlock(q); 03765 } 03766 AST_LIST_UNLOCK(&queues); 03767 03768 return found ? RESULT_SUCCESS : RESULT_FAILURE; 03769 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 495 of file app_queue.c.
References pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
00496 { 00497 int i; 00498 00499 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) { 00500 if (queue_results[i].id == res) { 00501 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 00502 return; 00503 } 00504 } 00505 }
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 2885 of file app_queue.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_lock, ast_channel_unlock, ast_log(), queue_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().
02886 { 02887 struct ast_datastore *ds; 02888 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 02889 02890 if (!qtds) { 02891 ast_log(LOG_WARNING, "Memory allocation error!\n"); 02892 return NULL; 02893 } 02894 02895 ast_channel_lock(qe->chan); 02896 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) { 02897 ast_channel_unlock(qe->chan); 02898 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 02899 return NULL; 02900 } 02901 02902 qtds->qe = qe; 02903 /* This member is refcounted in try_calling, so no need to add it here, too */ 02904 qtds->member = member; 02905 qtds->starttime = starttime; 02906 qtds->callcompletedinsl = callcompletedinsl; 02907 ds->data = qtds; 02908 ast_channel_datastore_add(qe->chan, ds); 02909 ast_channel_unlock(qe->chan); 02910 return ds; 02911 }
static int statechange_queue | ( | const char * | dev, | |
int | state, | |||
void * | ign | |||
) | [static] |
Producer of the statechange queue.
Definition at line 792 of file app_queue.c.
References ast_calloc, ast_cond_signal(), AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), statechange::dev, device_state, statechange::entry, and statechange::state.
Referenced by load_module(), and unload_module().
00793 { 00794 struct statechange *sc; 00795 00796 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1))) 00797 return 0; 00798 00799 sc->state = state; 00800 strcpy(sc->dev, dev); 00801 00802 ast_mutex_lock(&device_state.lock); 00803 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry); 00804 ast_cond_signal(&device_state.cond); 00805 ast_mutex_unlock(&device_state.lock); 00806 00807 return 0; 00808 }
static int store_next | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing | |||
) | [static] |
Definition at line 2181 of file app_queue.c.
References ast_log(), find_best(), callattempt::interface, LOG_DEBUG, callattempt::metric, option_debug, queue_ent::parent, call_queue::rrpos, and call_queue::wrapped.
Referenced by try_calling().
02182 { 02183 struct callattempt *best = find_best(outgoing); 02184 02185 if (best) { 02186 /* Ring just the best channel */ 02187 if (option_debug) 02188 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric); 02189 qe->parent->rrpos = best->metric % 1000; 02190 } else { 02191 /* Just increment rrpos */ 02192 if (qe->parent->wrapped) { 02193 /* No more channels, start over */ 02194 qe->parent->rrpos = 0; 02195 } else { 02196 /* Prioritize next entry */ 02197 qe->parent->rrpos++; 02198 } 02199 } 02200 qe->parent->wrapped = 0; 02201 02202 return 0; 02203 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 519 of file app_queue.c.
References name, and strategies.
Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_queues().
00520 { 00521 int x; 00522 00523 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) { 00524 if (!strcasecmp(strategy, strategies[x].name)) 00525 return strategies[x].strategy; 00526 } 00527 00528 return -1; 00529 }
static int try_calling | ( | struct queue_ent * | qe, | |
const char * | options, | |||
char * | announceoverride, | |||
const char * | url, | |||
int * | tries, | |||
int * | noption, | |||
const char * | agi | |||
) | [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] | 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 |
Definition at line 2938 of file app_queue.c.
References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_failed(), AST_CDR_FLAG_DONT_TOUCH, 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_alloc(), ast_channel_datastore_find(), ast_channel_datastore_free(), 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_clear_flag, ast_copy_string(), AST_DEVICE_NOT_INUSE, AST_DIGIT_ANY, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, ast_hangup(), 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_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, callattempt::oldstatus, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), queue_ent::pending, play_file(), queue_ent::pos, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, queue_transfer_info, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, call_queue::servicelevel, call_queue::setinterfacevar, setup_transfer_datastore(), call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, callattempt::stillgoing, store_next(), call_queue::strategy, tds, ast_channel::tech, call_queue::timeout, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), and wait_for_answer().
Referenced by queue_exec().
02939 { 02940 struct member *cur; 02941 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 02942 int to; 02943 char oldexten[AST_MAX_EXTENSION]=""; 02944 char oldcontext[AST_MAX_CONTEXT]=""; 02945 char queuename[256]=""; 02946 struct ast_channel *peer; 02947 struct ast_channel *which; 02948 struct callattempt *lpeer; 02949 struct member *member; 02950 struct ast_app *app; 02951 int res = 0, bridge = 0; 02952 int numbusies = 0; 02953 int x=0; 02954 char *announce = NULL; 02955 char digit = 0; 02956 time_t callstart; 02957 time_t now = time(NULL); 02958 struct ast_bridge_config bridge_config; 02959 char nondataquality = 1; 02960 char *agiexec = NULL; 02961 int ret = 0; 02962 const char *monitorfilename; 02963 const char *monitor_exec; 02964 const char *monitor_options; 02965 char tmpid[256], tmpid2[256]; 02966 char meid[1024], meid2[1024]; 02967 char mixmonargs[1512]; 02968 struct ast_app *mixmonapp = NULL; 02969 char *p; 02970 char vars[2048]; 02971 int forwardsallowed = 1; 02972 int callcompletedinsl; 02973 struct ao2_iterator memi; 02974 struct ast_datastore *datastore, *transfer_ds; 02975 const int need_weight = use_weight; 02976 02977 ast_channel_lock(qe->chan); 02978 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 02979 ast_channel_unlock(qe->chan); 02980 02981 memset(&bridge_config, 0, sizeof(bridge_config)); 02982 time(&now); 02983 02984 /* If we've already exceeded our timeout, then just stop 02985 * This should be extremely rare. queue_exec will take care 02986 * of removing the caller and reporting the timeout as the reason. 02987 */ 02988 if (qe->expire && now >= qe->expire) { 02989 res = 0; 02990 goto out; 02991 } 02992 02993 for (; options && *options; options++) 02994 switch (*options) { 02995 case 't': 02996 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 02997 break; 02998 case 'T': 02999 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 03000 break; 03001 case 'w': 03002 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 03003 break; 03004 case 'W': 03005 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 03006 break; 03007 case 'd': 03008 nondataquality = 0; 03009 break; 03010 case 'h': 03011 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 03012 break; 03013 case 'H': 03014 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 03015 break; 03016 case 'n': 03017 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) 03018 (*tries)++; 03019 else 03020 *tries = qe->parent->membercount; 03021 *noption = 1; 03022 break; 03023 case 'i': 03024 forwardsallowed = 0; 03025 break; 03026 } 03027 03028 /* Hold the lock while we setup the outgoing calls */ 03029 03030 if (need_weight) 03031 AST_LIST_LOCK(&queues); 03032 ao2_lock(qe->parent); 03033 if (option_debug) 03034 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 03035 qe->chan->name); 03036 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 03037 if (!ast_strlen_zero(qe->announce)) 03038 announce = qe->announce; 03039 if (!ast_strlen_zero(announceoverride)) 03040 announce = announceoverride; 03041 03042 memi = ao2_iterator_init(qe->parent->members, 0); 03043 while ((cur = ao2_iterator_next(&memi))) { 03044 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 03045 struct ast_dialed_interface *di; 03046 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 03047 if (!tmp) { 03048 ao2_iterator_destroy(&memi); 03049 ao2_ref(cur, -1); 03050 ao2_unlock(qe->parent); 03051 if (need_weight) 03052 AST_LIST_UNLOCK(&queues); 03053 goto out; 03054 } 03055 if (!datastore) { 03056 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) { 03057 ao2_iterator_destroy(&memi); 03058 ao2_ref(cur, -1); 03059 ao2_unlock(qe->parent); 03060 if (need_weight) 03061 AST_LIST_UNLOCK(&queues); 03062 free(tmp); 03063 goto out; 03064 } 03065 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 03066 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 03067 ao2_iterator_destroy(&memi); 03068 ao2_ref(cur, -1); 03069 ao2_unlock(qe->parent); 03070 if (need_weight) 03071 AST_LIST_UNLOCK(&queues); 03072 free(tmp); 03073 goto out; 03074 } 03075 datastore->data = dialed_interfaces; 03076 AST_LIST_HEAD_INIT(dialed_interfaces); 03077 03078 ast_channel_lock(qe->chan); 03079 ast_channel_datastore_add(qe->chan, datastore); 03080 ast_channel_unlock(qe->chan); 03081 } else 03082 dialed_interfaces = datastore->data; 03083 03084 AST_LIST_LOCK(dialed_interfaces); 03085 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 03086 if (!strcasecmp(cur->interface, di->interface)) { 03087 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 03088 di->interface); 03089 break; 03090 } 03091 } 03092 AST_LIST_UNLOCK(dialed_interfaces); 03093 03094 if (di) { 03095 free(tmp); 03096 continue; 03097 } 03098 03099 /* It is always ok to dial a Local interface. We only keep track of 03100 * which "real" interfaces have been dialed. The Local channel will 03101 * inherit this list so that if it ends up dialing a real interface, 03102 * it won't call one that has already been called. */ 03103 if (strncasecmp(cur->interface, "Local/", 6)) { 03104 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 03105 ao2_iterator_destroy(&memi); 03106 ao2_ref(cur, -1); 03107 ao2_unlock(qe->parent); 03108 if (need_weight) 03109 AST_LIST_UNLOCK(&queues); 03110 free(tmp); 03111 goto out; 03112 } 03113 strcpy(di->interface, cur->interface); 03114 03115 AST_LIST_LOCK(dialed_interfaces); 03116 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 03117 AST_LIST_UNLOCK(dialed_interfaces); 03118 } 03119 03120 tmp->stillgoing = -1; 03121 tmp->member = cur; 03122 tmp->oldstatus = cur->status; 03123 tmp->lastcall = cur->lastcall; 03124 tmp->lastqueue = cur->lastqueue; 03125 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 03126 if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) { 03127 cur->ringcount = 0; 03128 } 03129 /* Special case: If we ring everyone, go ahead and ring them, otherwise 03130 just calculate their metric for the appropriate strategy */ 03131 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 03132 /* Put them in the list of outgoing thingies... We're ready now. 03133 XXX If we're forcibly removed, these outgoing calls won't get 03134 hung up XXX */ 03135 tmp->q_next = outgoing; 03136 outgoing = tmp; 03137 /* If this line is up, don't try anybody else */ 03138 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 03139 break; 03140 } else { 03141 ao2_ref(cur, -1); 03142 free(tmp); 03143 } 03144 } 03145 ao2_iterator_destroy(&memi); 03146 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 03147 to = (qe->expire - now) * 1000; 03148 else 03149 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 03150 ++qe->pending; 03151 ++qe->tries; 03152 if (option_debug) 03153 ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n", 03154 qe->chan->name, queuename, qe->tries); 03155 ao2_unlock(qe->parent); 03156 ring_one(qe, outgoing, &numbusies); 03157 if (need_weight) 03158 AST_LIST_UNLOCK(&queues); 03159 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); 03160 /* The ast_channel_datastore_remove() function could fail here if the 03161 * datastore was moved to another channel during a masquerade. If this is 03162 * the case, don't free the datastore here because later, when the channel 03163 * to which the datastore was moved hangs up, it will attempt to free this 03164 * datastore again, causing a crash 03165 */ 03166 ast_channel_lock(qe->chan); 03167 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 03168 ast_channel_datastore_free(datastore); 03169 } 03170 ast_channel_unlock(qe->chan); 03171 ao2_lock(qe->parent); 03172 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) { 03173 store_next(qe, outgoing); 03174 } 03175 ao2_unlock(qe->parent); 03176 peer = lpeer ? lpeer->chan : NULL; 03177 if (!peer) { 03178 qe->pending = 0; 03179 if (to) { 03180 /* Must gotten hung up */ 03181 res = -1; 03182 } else { 03183 /* User exited by pressing a digit */ 03184 res = digit; 03185 } 03186 if (res == -1) { 03187 /* Post this CDR, and mark call as NOANSWER */ 03188 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH); 03189 ast_cdr_noanswer(qe->chan->cdr); 03190 if (queue_debug) 03191 ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name); 03192 } 03193 if (qe->parent->eventwhencalled) { 03194 manager_event(EVENT_FLAG_AGENT, "AgentTimeout", 03195 "Queue: %s\r\n" 03196 "ChannelCalling: %s\r\n" 03197 "Uniqueid: %s\r\n" 03198 "Tries: %d\r\n" 03199 "Holdtime: %ld\r\n", 03200 queuename, qe->chan->name, qe->chan->uniqueid, qe->tries, 03201 (long)time(NULL) - qe->start); 03202 } 03203 if (ast_cdr_isset_unanswered()) { 03204 /* channel contains the name of one of the outgoing channels 03205 in its CDR; zero out this CDR to avoid a dual-posting */ 03206 struct callattempt *o; 03207 for (o = outgoing; o; o = o->q_next) { 03208 if (!o->chan) { 03209 continue; 03210 } 03211 if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) { 03212 ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED); 03213 break; 03214 } 03215 } 03216 } 03217 } else { /* peer is valid */ 03218 /* Ah ha! Someone answered within the desired timeframe. Of course after this 03219 we will always return with -1 so that it is hung up properly after the 03220 conversation. */ 03221 if (!strcmp(qe->chan->tech->type, "Zap")) 03222 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 03223 if (!strcmp(peer->tech->type, "Zap")) 03224 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 03225 /* Update parameters for the queue */ 03226 time(&now); 03227 recalc_holdtime(qe, (now - qe->start)); 03228 ao2_lock(qe->parent); 03229 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 03230 ao2_unlock(qe->parent); 03231 member = lpeer->member; 03232 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 03233 ao2_ref(member, 1); 03234 hangupcalls(outgoing, peer); 03235 outgoing = NULL; 03236 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 03237 int res2; 03238 03239 res2 = ast_autoservice_start(qe->chan); 03240 if (!res2) { 03241 if (qe->parent->memberdelay) { 03242 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 03243 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 03244 } 03245 if (!res2 && announce) { 03246 play_file(peer, announce); 03247 } 03248 if (!res2 && qe->parent->reportholdtime) { 03249 if (!play_file(peer, qe->parent->sound_reporthold)) { 03250 int holdtime; 03251 03252 time(&now); 03253 holdtime = abs((now - qe->start) / 60); 03254 if (holdtime < 2) { 03255 play_file(peer, qe->parent->sound_lessthan); 03256 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL); 03257 } else 03258 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 03259 play_file(peer, qe->parent->sound_minutes); 03260 } 03261 } 03262 } 03263 res2 |= ast_autoservice_stop(qe->chan); 03264 if (peer->_softhangup) { 03265 /* Agent must have hung up */ 03266 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 03267 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 03268 if (qe->parent->eventwhencalled) 03269 manager_event(EVENT_FLAG_AGENT, "AgentDump", 03270 "Queue: %s\r\n" 03271 "Uniqueid: %s\r\n" 03272 "Channel: %s\r\n" 03273 "Member: %s\r\n" 03274 "MemberName: %s\r\n" 03275 "%s", 03276 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03277 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03278 ast_hangup(peer); 03279 ao2_ref(member, -1); 03280 goto out; 03281 } else if (res2) { 03282 /* Caller must have hung up just before being connected*/ 03283 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 03284 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 03285 record_abandoned(qe); 03286 ast_hangup(peer); 03287 ao2_ref(member, -1); 03288 return -1; 03289 } 03290 } 03291 /* Stop music on hold */ 03292 ast_moh_stop(qe->chan); 03293 /* If appropriate, log that we have a destination channel */ 03294 if (qe->chan->cdr) 03295 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 03296 /* Make sure channels are compatible */ 03297 res = ast_channel_make_compatible(qe->chan, peer); 03298 if (res < 0) { 03299 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 03300 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 03301 record_abandoned(qe); 03302 ast_cdr_failed(qe->chan->cdr); 03303 ast_hangup(peer); 03304 ao2_ref(member, -1); 03305 return -1; 03306 } 03307 03308 if (qe->parent->setinterfacevar) 03309 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface); 03310 03311 /* Begin Monitoring */ 03312 if (qe->parent->monfmt && *qe->parent->monfmt) { 03313 if (!qe->parent->montype) { 03314 if (option_debug) 03315 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n"); 03316 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 03317 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) 03318 which = qe->chan; 03319 else 03320 which = peer; 03321 if (monitorfilename) 03322 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); 03323 else if (qe->chan->cdr) 03324 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); 03325 else { 03326 /* Last ditch effort -- no CDR, make up something */ 03327 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03328 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 ); 03329 } 03330 if (qe->parent->monjoin) 03331 ast_monitor_setjoinfiles(which, 1); 03332 } else { 03333 if (option_debug) 03334 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n"); 03335 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 03336 if (!monitorfilename) { 03337 if (qe->chan->cdr) 03338 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1); 03339 else 03340 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03341 } else { 03342 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1); 03343 for (p = tmpid2; *p ; p++) { 03344 if (*p == '^' && *(p+1) == '{') { 03345 *p = '$'; 03346 } 03347 } 03348 03349 memset(tmpid, 0, sizeof(tmpid)); 03350 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 03351 } 03352 03353 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"); 03354 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"); 03355 03356 if (monitor_exec) { 03357 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1); 03358 for (p = meid2; *p ; p++) { 03359 if (*p == '^' && *(p+1) == '{') { 03360 *p = '$'; 03361 } 03362 } 03363 03364 memset(meid, 0, sizeof(meid)); 03365 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 03366 } 03367 03368 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt); 03369 03370 mixmonapp = pbx_findapp("MixMonitor"); 03371 03372 if (strchr(tmpid2, '|')) { 03373 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n"); 03374 mixmonapp = NULL; 03375 } 03376 03377 if (!monitor_options) 03378 monitor_options = ""; 03379 03380 if (strchr(monitor_options, '|')) { 03381 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n"); 03382 mixmonapp = NULL; 03383 } 03384 03385 if (mixmonapp) { 03386 if (!ast_strlen_zero(monitor_exec)) 03387 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec); 03388 else 03389 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options); 03390 03391 if (option_debug) 03392 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 03393 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 03394 if (qe->chan->cdr) 03395 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03396 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 03397 if (qe->chan->cdr) 03398 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03399 03400 } else 03401 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 03402 03403 } 03404 } 03405 /* Drop out of the queue at this point, to prepare for next caller */ 03406 leave_queue(qe); 03407 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 03408 if (option_debug) 03409 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); 03410 ast_channel_sendurl(peer, url); 03411 } 03412 if (!ast_strlen_zero(agi)) { 03413 if (option_debug) 03414 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi); 03415 app = pbx_findapp("agi"); 03416 if (app) { 03417 agiexec = ast_strdupa(agi); 03418 ret = pbx_exec(qe->chan, app, agiexec); 03419 } else 03420 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 03421 } 03422 qe->handled++; 03423 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid); 03424 if (qe->parent->eventwhencalled) 03425 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 03426 "Queue: %s\r\n" 03427 "Uniqueid: %s\r\n" 03428 "Channel: %s\r\n" 03429 "Member: %s\r\n" 03430 "MemberName: %s\r\n" 03431 "Holdtime: %ld\r\n" 03432 "BridgedChannel: %s\r\n" 03433 "%s", 03434 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03435 (long)time(NULL) - qe->start, peer->uniqueid, 03436 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03437 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 03438 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 03439 time(&callstart); 03440 03441 if (member->status == AST_DEVICE_NOT_INUSE) 03442 ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername); 03443 03444 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 03445 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 03446 03447 ast_channel_lock(qe->chan); 03448 if (!attended_transfer_occurred(qe->chan)) { 03449 struct ast_datastore *tds; 03450 03451 /* detect a blind transfer */ 03452 if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) { 03453 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 03454 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 03455 (long) (time(NULL) - callstart)); 03456 if (qe->parent->eventwhencalled) 03457 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03458 "Queue: %s\r\n" 03459 "Uniqueid: %s\r\n" 03460 "Channel: %s\r\n" 03461 "Member: %s\r\n" 03462 "MemberName: %s\r\n" 03463 "HoldTime: %ld\r\n" 03464 "TalkTime: %ld\r\n" 03465 "Reason: transfer\r\n" 03466 "%s", 03467 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03468 (long)(callstart - qe->start), (long)(time(NULL) - callstart), 03469 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03470 } else if (qe->chan->_softhangup) { 03471 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 03472 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03473 if (qe->parent->eventwhencalled) 03474 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03475 "Queue: %s\r\n" 03476 "Uniqueid: %s\r\n" 03477 "Channel: %s\r\n" 03478 "Member: %s\r\n" 03479 "MemberName: %s\r\n" 03480 "HoldTime: %ld\r\n" 03481 "TalkTime: %ld\r\n" 03482 "Reason: caller\r\n" 03483 "%s", 03484 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03485 (long)(callstart - qe->start), (long)(time(NULL) - callstart), 03486 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03487 } else { 03488 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 03489 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03490 if (qe->parent->eventwhencalled) 03491 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03492 "Queue: %s\r\n" 03493 "Uniqueid: %s\r\n" 03494 "Channel: %s\r\n" 03495 "Member: %s\r\n" 03496 "MemberName: %s\r\n" 03497 "HoldTime: %ld\r\n" 03498 "TalkTime: %ld\r\n" 03499 "Reason: agent\r\n" 03500 "%s", 03501 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start), 03502 (long)(time(NULL) - callstart), 03503 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03504 } 03505 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 03506 ast_channel_datastore_remove(qe->chan, tds); 03507 } 03508 update_queue(qe->parent, member, callcompletedinsl); 03509 } else { 03510 if (qe->parent->eventwhencalled) 03511 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03512 "Queue: %s\r\n" 03513 "Uniqueid: %s\r\n" 03514 "Channel: %s\r\n" 03515 "Member: %s\r\n" 03516 "MemberName: %s\r\n" 03517 "HoldTime: %ld\r\n" 03518 "TalkTime: %ld\r\n" 03519 "Reason: transfer\r\n" 03520 "%s", 03521 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start), 03522 (long)(time(NULL) - callstart), 03523 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03524 } 03525 03526 if (transfer_ds) { 03527 ast_channel_datastore_free(transfer_ds); 03528 } 03529 ast_channel_unlock(qe->chan); 03530 ast_hangup(peer); 03531 res = bridge ? bridge : 1; 03532 ao2_ref(member, -1); 03533 } 03534 out: 03535 hangupcalls(outgoing, NULL); 03536 03537 return res; 03538 }
static int unload_module | ( | void | ) | [static] |
Definition at line 5805 of file app_queue.c.
References ast_cli_unregister_multiple(), ast_cond_signal(), ast_custom_function_unregister(), ast_devstate_del(), ast_manager_unregister(), ast_module_user_hangup_all, ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state, queueagentcount_function, queueexists_function, queuemembercount_function, queuememberlist_function, queuememberpaused_function, queuememberstatus_function, queuewaitingcount_function, and statechange_queue().
05806 { 05807 int res; 05808 05809 if (device_state.thread != AST_PTHREADT_NULL) { 05810 device_state.stop = 1; 05811 ast_mutex_lock(&device_state.lock); 05812 ast_cond_signal(&device_state.cond); 05813 ast_mutex_unlock(&device_state.lock); 05814 pthread_join(device_state.thread, NULL); 05815 } 05816 05817 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 05818 res = ast_manager_unregister("QueueStatus"); 05819 res |= ast_manager_unregister("Queues"); 05820 res |= ast_manager_unregister("QueueAdd"); 05821 res |= ast_manager_unregister("QueueRemove"); 05822 res |= ast_manager_unregister("QueuePause"); 05823 res |= ast_unregister_application(app_aqm); 05824 res |= ast_unregister_application(app_rqm); 05825 res |= ast_unregister_application(app_pqm); 05826 res |= ast_unregister_application(app_upqm); 05827 res |= ast_unregister_application(app_ql); 05828 res |= ast_unregister_application(app); 05829 res |= ast_custom_function_unregister(&queueexists_function); 05830 res |= ast_custom_function_unregister(&queueagentcount_function); 05831 res |= ast_custom_function_unregister(&queuemembercount_function); 05832 res |= ast_custom_function_unregister(&queuememberlist_function); 05833 res |= ast_custom_function_unregister(&queuememberstatus_function); 05834 res |= ast_custom_function_unregister(&queuememberpaused_function); 05835 res |= ast_custom_function_unregister(&queuewaitingcount_function); 05836 ast_devstate_del(statechange_queue, NULL); 05837 05838 ast_module_user_hangup_all(); 05839 05840 clear_and_free_interfaces(); 05841 05842 return res; 05843 }
static int update_queue | ( | struct call_queue * | q, | |
struct member * | member, | |||
int | callcompletedinsl | |||
) | [static] |
Definition at line 2715 of file app_queue.c.
References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, and call_queue::members.
Referenced by queue_transfer_fixup(), and try_calling().
02716 { 02717 struct member *mem; 02718 struct call_queue *qtmp; 02719 02720 if (shared_lastcall) { 02721 AST_LIST_LOCK(&queues); 02722 AST_LIST_TRAVERSE(&queues, qtmp, list) { 02723 ao2_lock(qtmp); 02724 if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) { 02725 time(&mem->lastcall); 02726 mem->calls++; 02727 mem->lastqueue = q; 02728 ao2_ref(mem, -1); 02729 } 02730 ao2_unlock(qtmp); 02731 } 02732 AST_LIST_UNLOCK(&queues); 02733 } else { 02734 ao2_lock(q); 02735 time(&member->lastcall); 02736 member->calls++; 02737 member->lastqueue = q; 02738 ao2_unlock(q); 02739 } 02740 ao2_lock(q); 02741 q->callscompleted++; 02742 if (callcompletedinsl) 02743 q->callscompletedinsl++; 02744 ao2_unlock(q); 02745 02746 return 0; 02747 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 1399 of file app_queue.c.
References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), ast_variables_destroy(), member::interface, ast_variable::name, ast_variable::next, ast_variable::value, and var.
Referenced by set_member_paused().
01400 { 01401 struct ast_variable *var, *save; 01402 int ret = -1; 01403 01404 if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 01405 return ret; 01406 save = var; 01407 while (var) { 01408 if (!strcmp(var->name, "uniqueid")) 01409 break; 01410 var = var->next; 01411 } 01412 if (var && !ast_strlen_zero(var->value)) { 01413 if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1) 01414 ret = 0; 01415 } 01416 ast_variables_destroy(save); 01417 return ret; 01418 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 1420 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_load_realtime_multientry(), ast_log(), ast_variable_retrieve(), member::dead, member_interface::interface, LOG_DEBUG, call_queue::membercount, call_queue::members, call_queue::name, option_debug, member::realtime, remove_from_interfaces(), rt_handle_member_record(), S_OR, and member::state_interface.
Referenced by load_realtime_queue(), and queue_exec().
01421 { 01422 struct ast_config *member_config = NULL; 01423 struct member *m; 01424 char *interface = NULL; 01425 struct ao2_iterator mem_iter; 01426 01427 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) { 01428 /*This queue doesn't have realtime members*/ 01429 if (option_debug > 2) 01430 ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name); 01431 return; 01432 } 01433 01434 ao2_lock(q); 01435 01436 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 01437 mem_iter = ao2_iterator_init(q->members, 0); 01438 while ((m = ao2_iterator_next(&mem_iter))) { 01439 if (m->realtime) 01440 m->dead = 1; 01441 ao2_ref(m, -1); 01442 } 01443 ao2_iterator_destroy(&mem_iter); 01444 01445 while ((interface = ast_category_browse(member_config, interface))) { 01446 rt_handle_member_record(q, interface, 01447 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 01448 ast_variable_retrieve(member_config, interface, "penalty"), 01449 ast_variable_retrieve(member_config, interface, "paused"), 01450 S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface)); 01451 } 01452 01453 /* Delete all realtime members that have been deleted in DB. */ 01454 mem_iter = ao2_iterator_init(q->members, 0); 01455 while ((m = ao2_iterator_next(&mem_iter))) { 01456 if (m->dead) { 01457 ao2_unlink(q->members, m); 01458 ao2_unlock(q); 01459 remove_from_interfaces(m->state_interface); 01460 ao2_lock(q); 01461 q->membercount--; 01462 } 01463 ao2_ref(m, -1); 01464 } 01465 ao2_iterator_destroy(&mem_iter); 01466 ao2_unlock(q); 01467 ast_config_destroy(member_config); 01468 }
static int update_status | ( | const char * | interface, | |
const int | status | |||
) | [static] |
Definition at line 643 of file app_queue.c.
References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, member::state_interface, and member::status.
Referenced by handle_statechange(), and ring_entry().
00644 { 00645 struct member *cur; 00646 struct ao2_iterator mem_iter; 00647 struct call_queue *q; 00648 char tmp_interface[80]; 00649 00650 AST_LIST_LOCK(&queues); 00651 AST_LIST_TRAVERSE(&queues, q, list) { 00652 ao2_lock(q); 00653 mem_iter = ao2_iterator_init(q->members, 0); 00654 while ((cur = ao2_iterator_next(&mem_iter))) { 00655 char *slash_pos; 00656 ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface)); 00657 if ((slash_pos = strchr(tmp_interface, '/'))) 00658 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00659 *slash_pos = '\0'; 00660 00661 if (strcasecmp(interface, tmp_interface)) { 00662 ao2_ref(cur, -1); 00663 continue; 00664 } 00665 00666 if (cur->status != status) { 00667 cur->status = status; 00668 if (q->maskmemberstatus) { 00669 ao2_ref(cur, -1); 00670 continue; 00671 } 00672 00673 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 00674 "Queue: %s\r\n" 00675 "Location: %s\r\n" 00676 "MemberName: %s\r\n" 00677 "Membership: %s\r\n" 00678 "Penalty: %d\r\n" 00679 "CallsTaken: %d\r\n" 00680 "LastCall: %d\r\n" 00681 "Status: %d\r\n" 00682 "Paused: %d\r\n", 00683 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", 00684 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); 00685 } 00686 ao2_ref(cur, -1); 00687 } 00688 ao2_iterator_destroy(&mem_iter); 00689 ao2_unlock(q); 00690 } 00691 AST_LIST_UNLOCK(&queues); 00692 00693 return 0; 00694 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3927 of file app_queue.c.
References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().
Referenced by load_module().
03928 { 03929 struct ast_module_user *lu; 03930 char *parse; 03931 int priority_jump = 0; 03932 int ignore_fail = 0; 03933 AST_DECLARE_APP_ARGS(args, 03934 AST_APP_ARG(queuename); 03935 AST_APP_ARG(interface); 03936 AST_APP_ARG(options); 03937 ); 03938 03939 if (ast_strlen_zero(data)) { 03940 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03941 return -1; 03942 } 03943 03944 parse = ast_strdupa(data); 03945 03946 AST_STANDARD_APP_ARGS(args, parse); 03947 03948 lu = ast_module_user_add(chan); 03949 03950 if (args.options) { 03951 if (strchr(args.options, 'j')) 03952 priority_jump = 1; 03953 if (strchr(args.options, 'i')) 03954 ignore_fail = 1; 03955 } 03956 03957 if (ast_strlen_zero(args.interface)) { 03958 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03959 ast_module_user_remove(lu); 03960 return -1; 03961 } 03962 03963 if (set_member_paused(args.queuename, args.interface, 0)) { 03964 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 03965 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 03966 if (priority_jump || ast_opt_priority_jumping) { 03967 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03968 ast_module_user_remove(lu); 03969 return 0; 03970 } 03971 } 03972 ast_module_user_remove(lu); 03973 if (ignore_fail) { 03974 return 0; 03975 } else { 03976 return -1; 03977 } 03978 } 03979 03980 ast_module_user_remove(lu); 03981 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 03982 return 0; 03983 }
static int valid_exit | ( | struct queue_ent * | qe, | |
char | digit | |||
) | [static] |
Definition at line 1608 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().
01609 { 01610 int digitlen = strlen(qe->digits); 01611 01612 /* Prevent possible buffer overflow */ 01613 if (digitlen < sizeof(qe->digits) - 2) { 01614 qe->digits[digitlen] = digit; 01615 qe->digits[digitlen + 1] = '\0'; 01616 } else { 01617 qe->digits[0] = '\0'; 01618 return 0; 01619 } 01620 01621 /* If there's no context to goto, short-circuit */ 01622 if (ast_strlen_zero(qe->context)) 01623 return 0; 01624 01625 /* If the extension is bad, then reset the digits to blank */ 01626 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) { 01627 qe->digits[0] = '\0'; 01628 return 0; 01629 } 01630 01631 /* We have an exact match */ 01632 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 01633 qe->valid_digits = 1; 01634 /* Return 1 on a successful goto */ 01635 return 1; 01636 } 01637 01638 return 0; 01639 }
static char* vars2manager | ( | struct ast_channel * | chan, | |
char * | vars, | |||
size_t | len | |||
) | [static] |
Definition at line 1913 of file app_queue.c.
References ast_copy_string(), and pbx_builtin_serialize_variables().
Referenced by ring_entry(), and try_calling().
01914 { 01915 char *tmp = alloca(len); 01916 01917 if (pbx_builtin_serialize_variables(chan, tmp, len)) { 01918 int i, j; 01919 01920 /* convert "\n" to "\nVariable: " */ 01921 strcpy(vars, "Variable: "); 01922 01923 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 01924 vars[j] = tmp[i]; 01925 01926 if (tmp[i + 1] == '\0') 01927 break; 01928 if (tmp[i] == '\n') { 01929 vars[j++] = '\r'; 01930 vars[j++] = '\n'; 01931 01932 ast_copy_string(&(vars[j]), "Variable: ", len - j); 01933 j += 9; 01934 } 01935 } 01936 if (j > len - 3) 01937 j = len - 3; 01938 vars[j++] = '\r'; 01939 vars[j++] = '\n'; 01940 vars[j] = '\0'; 01941 } else { 01942 /* there are no channel variables; leave it blank */ 01943 *vars = '\0'; 01944 } 01945 return vars; 01946 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 3540 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
03541 { 03542 /* Don't need to hold the lock while we setup the outgoing calls */ 03543 int retrywait = qe->parent->retry * 1000; 03544 03545 int res = ast_waitfordigit(qe->chan, retrywait); 03546 if (res > 0 && !valid_exit(qe, res)) 03547 res = 0; 03548 03549 return res; 03550 }
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 2298 of file app_queue.c.
References ast_channel::_state, accountcode, ast_call(), ast_cdr_busy(), ast_cdr_failed(), ast_cdr_noanswer(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_check_hangup(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, ast_copy_string(), AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_indicate(), ast_log(), AST_MAX_WATCHERS, ast_moh_stop(), ast_read(), ast_request(), AST_STATE_UP, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verbose(), ast_waitfor_n(), callattempt::call_next, ast_channel::cdr, ast_channel::cdrflags, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, do_hang(), ast_channel::exten, f, ast_frame::frametype, free, callattempt::interface, member::interface, LOG_DEBUG, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_verbose, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), queue_ent::ring_when_ringing, rna(), S_OR, starttime, callattempt::stillgoing, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, valid_exit(), and VERBOSE_PREFIX_3.
Referenced by try_calling().
02299 { 02300 char *queue = qe->parent->name; 02301 struct callattempt *o, *start = NULL, *prev = NULL; 02302 int status; 02303 int numbusies = prebusies; 02304 int numnochan = 0; 02305 int stillgoing = 0; 02306 int orig = *to; 02307 struct ast_frame *f; 02308 struct callattempt *peer = NULL; 02309 struct ast_channel *winner; 02310 struct ast_channel *in = qe->chan; 02311 char on[80] = ""; 02312 char membername[80] = ""; 02313 long starttime = 0; 02314 long endtime = 0; 02315 02316 starttime = (long) time(NULL); 02317 02318 while (*to && !peer) { 02319 int numlines, retry, pos = 1; 02320 struct ast_channel *watchers[AST_MAX_WATCHERS]; 02321 watchers[0] = in; 02322 start = NULL; 02323 02324 for (retry = 0; retry < 2; retry++) { 02325 numlines = 0; 02326 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 02327 if (o->stillgoing) { /* Keep track of important channels */ 02328 stillgoing = 1; 02329 if (o->chan) { 02330 watchers[pos++] = o->chan; 02331 if (!start) 02332 start = o; 02333 else 02334 prev->call_next = o; 02335 prev = o; 02336 } 02337 } 02338 numlines++; 02339 } 02340 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 02341 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 02342 break; 02343 /* On "ringall" strategy we only move to the next penalty level 02344 when *all* ringing phones are done in the current penalty level */ 02345 ring_one(qe, outgoing, &numbusies); 02346 /* and retry... */ 02347 } 02348 if (pos == 1 /* not found */) { 02349 if (numlines == (numbusies + numnochan)) { 02350 ast_log(LOG_DEBUG, "Everyone is busy at this time\n"); 02351 if (in->cdr && (in->_state != AST_STATE_UP)) { 02352 ast_cdr_busy(in->cdr); 02353 } 02354 } else { 02355 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan); 02356 if (in->cdr && (in->_state != AST_STATE_UP)) { 02357 ast_cdr_failed(in->cdr); 02358 } 02359 } 02360 *to = 0; 02361 return NULL; 02362 } 02363 02364 /* Poll for events from both the incoming channel as well as any outgoing channels */ 02365 winner = ast_waitfor_n(watchers, pos, to); 02366 02367 /* Service all of the outgoing channels */ 02368 for (o = start; o; o = o->call_next) { 02369 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 02370 if (!peer) { 02371 if (option_verbose > 2) 02372 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02373 peer = o; 02374 } 02375 } else if (o->chan && (o->chan == winner)) { 02376 02377 ast_copy_string(on, o->member->interface, sizeof(on)); 02378 ast_copy_string(membername, o->member->membername, sizeof(membername)); 02379 02380 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 02381 if (option_verbose > 2) 02382 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); 02383 numnochan++; 02384 do_hang(o); 02385 winner = NULL; 02386 continue; 02387 } else if (!ast_strlen_zero(o->chan->call_forward)) { 02388 char tmpchan[256]; 02389 char *stuff; 02390 char *tech; 02391 02392 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 02393 if ((stuff = strchr(tmpchan, '/'))) { 02394 *stuff++ = '\0'; 02395 tech = tmpchan; 02396 } else { 02397 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 02398 stuff = tmpchan; 02399 tech = "Local"; 02400 } 02401 /* Before processing channel, go ahead and check for forwarding */ 02402 if (option_verbose > 2) 02403 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); 02404 /* Setup parameters */ 02405 o->chan = ast_request(tech, in->nativeformats, stuff, &status); 02406 if (!o->chan) { 02407 ast_log(LOG_NOTICE, 02408 "Forwarding failed to create channel to dial '%s/%s'\n", 02409 tech, stuff); 02410 o->stillgoing = 0; 02411 numnochan++; 02412 } else { 02413 ast_channel_inherit_variables(in, o->chan); 02414 ast_channel_datastore_inherit(in, o->chan); 02415 if (o->chan->cid.cid_num) 02416 free(o->chan->cid.cid_num); 02417 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); 02418 02419 if (o->chan->cid.cid_name) 02420 free(o->chan->cid.cid_name); 02421 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); 02422 02423 ast_string_field_set(o->chan, accountcode, in->accountcode); 02424 o->chan->cdrflags = in->cdrflags; 02425 02426 if (in->cid.cid_ani) { 02427 if (o->chan->cid.cid_ani) 02428 free(o->chan->cid.cid_ani); 02429 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); 02430 } 02431 if (o->chan->cid.cid_rdnis) 02432 free(o->chan->cid.cid_rdnis); 02433 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); 02434 if (ast_call(o->chan, stuff, 0)) { 02435 ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n", 02436 tech, stuff); 02437 do_hang(o); 02438 numnochan++; 02439 } 02440 } 02441 /* Hangup the original channel now, in case we needed it */ 02442 ast_hangup(winner); 02443 continue; 02444 } 02445 f = ast_read(winner); 02446 if (f) { 02447 if (f->frametype == AST_FRAME_CONTROL) { 02448 switch (f->subclass) { 02449 case AST_CONTROL_ANSWER: 02450 /* This is our guy if someone answered. */ 02451 if (!peer) { 02452 if (option_verbose > 2) 02453 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02454 peer = o; 02455 } 02456 break; 02457 case AST_CONTROL_BUSY: 02458 if (option_verbose > 2) 02459 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); 02460 if (in->cdr) 02461 ast_cdr_busy(in->cdr); 02462 do_hang(o); 02463 endtime = (long)time(NULL); 02464 endtime -= starttime; 02465 rna(endtime * 1000, qe, on, membername, 0); 02466 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02467 if (qe->parent->timeoutrestart) 02468 *to = orig; 02469 /* Have enough time for a queue member to answer? */ 02470 if (*to > 500) { 02471 ring_one(qe, outgoing, &numbusies); 02472 starttime = (long) time(NULL); 02473 } 02474 } 02475 numbusies++; 02476 break; 02477 case AST_CONTROL_CONGESTION: 02478 if (option_verbose > 2) 02479 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name); 02480 if (in->cdr) 02481 ast_cdr_failed(in->cdr); 02482 endtime = (long)time(NULL); 02483 endtime -= starttime; 02484 rna(endtime * 1000, qe, on, membername, 0); 02485 do_hang(o); 02486 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02487 if (qe->parent->timeoutrestart) 02488 *to = orig; 02489 if (*to > 500) { 02490 ring_one(qe, outgoing, &numbusies); 02491 starttime = (long) time(NULL); 02492 } 02493 } 02494 numbusies++; 02495 break; 02496 case AST_CONTROL_RINGING: 02497 if (option_verbose > 2) 02498 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); 02499 02500 /* Start ring indication when the channel is ringing, if specified */ 02501 if (qe->ring_when_ringing) { 02502 ast_moh_stop(qe->chan); 02503 ast_indicate(qe->chan, AST_CONTROL_RINGING); 02504 } 02505 break; 02506 case AST_CONTROL_OFFHOOK: 02507 /* Ignore going off hook */ 02508 break; 02509 default: 02510 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); 02511 } 02512 } 02513 ast_frfree(f); 02514 } else { /* ast_read() returned NULL */ 02515 endtime = (long) time(NULL) - starttime; 02516 rna(endtime * 1000, qe, on, membername, 1); 02517 do_hang(o); 02518 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02519 if (qe->parent->timeoutrestart) 02520 *to = orig; 02521 if (*to > 500) { 02522 ring_one(qe, outgoing, &numbusies); 02523 starttime = (long) time(NULL); 02524 } 02525 } 02526 } 02527 } 02528 } 02529 02530 /* If we received an event from the caller, deal with it. */ 02531 if (winner == in) { 02532 f = ast_read(in); 02533 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02534 /* Got hung up */ 02535 *to = -1; 02536 if (f) 02537 ast_frfree(f); 02538 return NULL; 02539 } 02540 /* First check if DTMF digit is a valid exit */ 02541 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) { 02542 if (option_verbose > 3) 02543 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass); 02544 *to = 0; 02545 *digit = f->subclass; 02546 ast_frfree(f); 02547 if (in->cdr && (in->_state != AST_STATE_UP)) { 02548 ast_cdr_noanswer(in->cdr); 02549 } 02550 return NULL; 02551 } 02552 /* Else check if DTMF should be interpreted as caller disconnect */ 02553 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { 02554 if (option_verbose > 3) 02555 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); 02556 *to = 0; 02557 ast_frfree(f); 02558 if (in->cdr && (in->_state != AST_STATE_UP)) { 02559 ast_cdr_noanswer(in->cdr); 02560 } 02561 return NULL; 02562 } 02563 ast_frfree(f); 02564 } 02565 if (!*to) { 02566 for (o = start; o; o = o->call_next) 02567 rna(orig, qe, o->interface, o->member->membername, 1); 02568 } 02569 } 02570 02571 if (in->cdr 02572 && in->_state != AST_STATE_UP 02573 && (!*to || ast_check_hangup(in))) { 02574 ast_cdr_noanswer(in->cdr); 02575 } 02576 02577 return peer; 02578 }
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 2640 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, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, and valid_exit().
Referenced by queue_exec().
02641 { 02642 int res = 0; 02643 02644 /* This is the holding pen for callers 2 through maxlen */ 02645 for (;;) { 02646 enum queue_member_status stat; 02647 02648 if (is_our_turn(qe)) 02649 break; 02650 02651 /* If we have timed out, break out */ 02652 if (qe->expire && (time(NULL) >= qe->expire)) { 02653 *reason = QUEUE_TIMEOUT; 02654 break; 02655 } 02656 02657 stat = get_member_status(qe->parent, qe->max_penalty); 02658 02659 /* leave the queue if no agents, if enabled */ 02660 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 02661 *reason = QUEUE_LEAVEEMPTY; 02662 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02663 leave_queue(qe); 02664 break; 02665 } 02666 02667 /* leave the queue if no reachable agents, if enabled */ 02668 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 02669 *reason = QUEUE_LEAVEUNAVAIL; 02670 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02671 leave_queue(qe); 02672 break; 02673 } 02674 02675 /* Make a position announcement, if enabled */ 02676 if (qe->parent->announcefrequency && !ringing && 02677 (res = say_position(qe))) 02678 break; 02679 02680 /* If we have timed out, break out */ 02681 if (qe->expire && (time(NULL) >= qe->expire)) { 02682 *reason = QUEUE_TIMEOUT; 02683 break; 02684 } 02685 02686 /* Make a periodic announcement, if enabled */ 02687 if (qe->parent->periodicannouncefrequency && !ringing && 02688 (res = say_periodic_announcement(qe))) 02689 break; 02690 02691 /* If we have timed out, break out */ 02692 if (qe->expire && (time(NULL) >= qe->expire)) { 02693 *reason = QUEUE_TIMEOUT; 02694 break; 02695 } 02696 02697 /* Wait a second before checking again */ 02698 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 02699 if (res > 0 && !valid_exit(qe, res)) 02700 res = 0; 02701 else 02702 break; 02703 } 02704 02705 /* If we have timed out, break out */ 02706 if (qe->expire && (time(NULL) >= qe->expire)) { 02707 *reason = QUEUE_TIMEOUT; 02708 break; 02709 } 02710 } 02711 02712 return res; 02713 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 5894 of file app_queue.c.
char* app = "Queue" [static] |
Definition at line 150 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 195 of file app_queue.c.
char* app_aqm_descrip [static] |
Definition at line 197 of file app_queue.c.
char* app_aqm_synopsis = "Dynamically adds queue members" [static] |
Definition at line 196 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 230 of file app_queue.c.
char* app_pqm_descrip [static] |
Definition at line 232 of file app_queue.c.
char* app_pqm_synopsis = "Pauses a queue member" [static] |
Definition at line 231 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 269 of file app_queue.c.
char* app_ql_descrip [static] |
" QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n" "Allows you to write your own events into the queue log\n" "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n"
Definition at line 271 of file app_queue.c.
char* app_ql_synopsis = "Writes to the queue_log" [static] |
Definition at line 270 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 214 of file app_queue.c.
char* app_rqm_descrip [static] |
Definition at line 216 of file app_queue.c.
char* app_rqm_synopsis = "Dynamically removes queue members" [static] |
Definition at line 215 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 253 of file app_queue.c.
char* app_upqm_descrip [static] |
Definition at line 255 of file app_queue.c.
char* app_upqm_synopsis = "Unpauses a queue member" [static] |
Definition at line 254 of file app_queue.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 5894 of file app_queue.c.
int autofill_default = 0 [static] |
queues.conf [general] option
Definition at line 291 of file app_queue.c.
struct ast_cli_entry cli_add_queue_member_deprecated [static] |
{ { "add", "queue", "member", NULL }, handle_queue_add_member, NULL, NULL, complete_queue_add_member }
Definition at line 5771 of file app_queue.c.
struct ast_cli_entry cli_queue[] [static] |
Definition at line 5781 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_cli_entry cli_remove_queue_member_deprecated [static] |
{ { "remove", "queue", "member", NULL }, handle_queue_remove_member, NULL, NULL, complete_queue_remove_member }
Definition at line 5776 of file app_queue.c.
struct ast_cli_entry cli_show_queue_deprecated [static] |
{ { "show", "queue", NULL }, queue_show, NULL, NULL, complete_queue_show }
Definition at line 5766 of file app_queue.c.
Condition for the state change queue
Definition at line 750 of file app_queue.c.
char* descrip [static] |
Definition at line 154 of file app_queue.c.
struct { ... } device_state [static] |
Data used by the device state thread.
Referenced by device_state_thread(), load_module(), statechange_queue(), and unload_module().
struct statechange* first |
Definition at line 752 of file app_queue.c.
enum queue_result id |
Definition at line 310 of file app_queue.c.
Referenced by _sip_show_peers().
struct statechange* last |
Definition at line 752 of file app_queue.c.
Lock for the state change queue
Definition at line 748 of file app_queue.c.
int montype_default = 0 [static] |
queues.conf [general] option
Definition at line 294 of file app_queue.c.
const char* pm_family = "Queue/PersistentMembers" [static] |
Persistent Members astdb family.
Definition at line 277 of file app_queue.c.
char qam_cmd_usage[] [static] |
"Usage: queue add member <channel> to <queue> [penalty <penalty> [as <membername> [state_interface <state_interface>]]]\n"
Definition at line 5760 of file app_queue.c.
char qmc_cmd_usage[] [static] |
"Usage: queue member count <queue>\n" " Provides member count information on a specified queue.\n"
Definition at line 5737 of file app_queue.c.
char qrm_cmd_usage[] [static] |
"Usage: queue remove member <channel> from <queue>\n"
Definition at line 5763 of file app_queue.c.
int queue_debug = 0 [static] |
queues.conf [general] extra debug option
Definition at line 282 of file app_queue.c.
int queue_persistent_members = 0 [static] |
queues.conf [general] option
Definition at line 285 of file app_queue.c.
struct { ... } queue_results[] |
Referenced by set_queue_result().
char queue_show_usage[] [static] |
"Usage: queue show\n" " Provides summary information on a specified queue.\n"
Definition at line 5756 of file app_queue.c.
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 2832 of file app_queue.c.
Referenced by attended_transfer_occurred(), queue_transfer_fixup(), setup_transfer_datastore(), and try_calling().
struct ast_custom_function queueagentcount_function [static] |
Definition at line 4778 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queueexists_function [static] |
Definition at line 4770 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuemembercount_function [static] |
Definition at line 4788 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberlist_function [static] |
Definition at line 4812 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberpaused_function [static] |
Definition at line 4830 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuememberstatus_function [static] |
Definition at line 4821 of file app_queue.c.
Referenced by load_module(), and unload_module().
struct ast_custom_function queuewaitingcount_function [static] |
Definition at line 4803 of file app_queue.c.
Referenced by load_module(), and unload_module().
int shared_lastcall = 0 [static] |
queues.conf [general] option
Definition at line 297 of file app_queue.c.
struct { ... } state_change_q |
Queue of state changes
unsigned int stop |
Set to 1 to stop the thread
Definition at line 744 of file app_queue.c.
struct strategy strategies[] [static] |
Referenced by int2strat(), and strat2int().
char* synopsis = "Queue a call for a call queue" [static] |
Definition at line 152 of file app_queue.c.
char* text |
Definition at line 311 of file app_queue.c.
Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), handle_response(), method_match(), parse_sip_options(), reqprep(), sendtext_exec(), set_queue_result(), sip_alloc(), and sip_show_channel().
pthread_t thread |
The device state monitoring thread
Definition at line 746 of file app_queue.c.
int use_weight = 0 [static] |
queues.conf per-queue weight option
Definition at line 288 of file app_queue.c.