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