#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_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 } |
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) |
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 | 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_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) |
allocate space for new queue member and set fields based on parameters passed | |
static void | destroy_queue (struct call_queue *q) |
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_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 | 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 | queue_exec (struct ast_channel *chan, void *data) |
The starting point for all queue calls. | |
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_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len) |
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 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) |
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 = "f450f61f60e761b3aa089ebed76ca8a5" , .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 { | |
statechange * first | |
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 | 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 | queuemembercount_function |
static struct ast_custom_function | queuememberlist_function |
static struct ast_custom_function | queuewaitingcount_function |
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 |
These features added by David C. Troy <dave@toad.net>:
Added servicelevel statistic by Michiel Betel <michiel@betel.nl> Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>
Fixed to work with CVS as of 2004-02-25 and released as 1.07a by Matthew Enger <m.enger@xi.com.au>
Definition in file app_queue.c.
#define ANNOUNCEHOLDTIME_ALWAYS 1 |
#define ANNOUNCEHOLDTIME_ONCE 2 |
#define AST_MAX_WATCHERS 256 |
Definition at line 2111 of file app_queue.c.
#define DEFAULT_RETRY 5 |
#define DEFAULT_TIMEOUT 15 |
#define MAX_PERIODIC_ANNOUNCEMENTS 10 |
Definition at line 140 of file app_queue.c.
Referenced by init_queue(), queue_set_param(), and say_periodic_announcement().
#define PM_MAX_LEN 8192 |
Definition at line 273 of file app_queue.c.
Referenced by dump_queue_members(), and reload_queue_members().
#define QUEUE_EMPTY_NORMAL 1 |
#define QUEUE_EMPTY_STRICT 2 |
Definition at line 385 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 388 of file app_queue.c.
Referenced by queue_set_param(), ring_entry(), and try_calling().
#define RECHECK 1 |
#define RES_EXISTS (-1) |
Definition at line 143 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 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_NOT_DYNAMIC (-4) |
Definition at line 146 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 142 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 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(), and reload_queue_members().
anonymous enum |
QUEUE_STRATEGY_RINGALL | |
QUEUE_STRATEGY_ROUNDROBIN | |
QUEUE_STRATEGY_LEASTRECENT | |
QUEUE_STRATEGY_FEWESTCALLS | |
QUEUE_STRATEGY_RANDOM | |
QUEUE_STRATEGY_RRMEMORY |
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 };
enum qmc_status |
Definition at line 4170 of file app_queue.c.
04170 { 04171 QMC_VALID = 0, /* Count valid members */ 04172 QMC_PAUSED, /* Count paused members */ 04173 QMC_ACTIVE, /* Count active members */ 04174 QMC_FREE, /* Count free members */ 04175 QMC_ALL /* Count all queue members */ 04176 };
enum queue_member_status |
Definition at line 539 of file app_queue.c.
00539 { 00540 QUEUE_NO_MEMBERS, 00541 QUEUE_NO_REACHABLE_MEMBERS, 00542 QUEUE_NORMAL 00543 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL |
Definition at line 290 of file app_queue.c.
00290 { 00291 QUEUE_UNKNOWN = 0, 00292 QUEUE_TIMEOUT = 1, 00293 QUEUE_JOINEMPTY = 2, 00294 QUEUE_LEAVEEMPTY = 3, 00295 QUEUE_JOINUNAVAIL = 4, 00296 QUEUE_LEAVEUNAVAIL = 5, 00297 QUEUE_FULL = 6, 00298 };
static int __queues_show | ( | struct mansession * | s, | |
int | manager, | |||
int | fd, | |||
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 4587 of file app_queue.c.
References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), 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_mutex_lock(), ast_mutex_unlock(), 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::lock, call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::prio, queue_show(), member::realtime, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::ringlimit, s, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
04588 { 04589 struct call_queue *q; 04590 struct queue_ent *qe; 04591 struct member *mem; 04592 int pos, queue_show; 04593 time_t now; 04594 char max_buf[150]; 04595 char *max; 04596 size_t max_left; 04597 float sl = 0; 04598 char *term = manager ? "\r\n" : "\n"; 04599 struct ao2_iterator mem_iter; 04600 04601 time(&now); 04602 if (argc == 2) 04603 queue_show = 0; 04604 else if (argc == 3) 04605 queue_show = 1; 04606 else 04607 return RESULT_SHOWUSAGE; 04608 04609 /* We only want to load realtime queues when a specific queue is asked for. */ 04610 if (queue_show) { 04611 load_realtime_queue(argv[2]); 04612 } else if (ast_check_realtime("queues")) { 04613 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL); 04614 char *queuename; 04615 if (cfg) { 04616 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 04617 load_realtime_queue(queuename); 04618 } 04619 ast_config_destroy(cfg); 04620 } 04621 } 04622 04623 AST_LIST_LOCK(&queues); 04624 if (AST_LIST_EMPTY(&queues)) { 04625 AST_LIST_UNLOCK(&queues); 04626 if (queue_show) { 04627 if (s) 04628 astman_append(s, "No such queue: %s.%s",argv[2], term); 04629 else 04630 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 04631 } else { 04632 if (s) 04633 astman_append(s, "No queues.%s", term); 04634 else 04635 ast_cli(fd, "No queues.%s", term); 04636 } 04637 return RESULT_SUCCESS; 04638 } 04639 AST_LIST_TRAVERSE(&queues, q, list) { 04640 ast_mutex_lock(&q->lock); 04641 if (queue_show) { 04642 if (strcasecmp(q->name, argv[2]) != 0) { 04643 ast_mutex_unlock(&q->lock); 04644 if (!AST_LIST_NEXT(q, list)) { 04645 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 04646 break; 04647 } 04648 continue; 04649 } 04650 } 04651 max_buf[0] = '\0'; 04652 max = max_buf; 04653 max_left = sizeof(max_buf); 04654 if (q->maxlen) 04655 ast_build_string(&max, &max_left, "%d", q->maxlen); 04656 else 04657 ast_build_string(&max, &max_left, "unlimited"); 04658 sl = 0; 04659 if (q->callscompleted > 0) 04660 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 04661 if (s) 04662 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", 04663 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit, 04664 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term); 04665 else 04666 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", 04667 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit, 04668 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term); 04669 if (ao2_container_count(q->members)) { 04670 if (s) 04671 astman_append(s, " Members: %s", term); 04672 else 04673 ast_cli(fd, " Members: %s", term); 04674 mem_iter = ao2_iterator_init(q->members, 0); 04675 while ((mem = ao2_iterator_next(&mem_iter))) { 04676 max_buf[0] = '\0'; 04677 max = max_buf; 04678 max_left = sizeof(max_buf); 04679 if (strcasecmp(mem->membername, mem->interface)) { 04680 ast_build_string(&max, &max_left, " (%s)", mem->interface); 04681 } 04682 if (mem->penalty) 04683 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty); 04684 if (mem->dynamic) 04685 ast_build_string(&max, &max_left, " (dynamic)"); 04686 if (mem->realtime) 04687 ast_build_string(&max, &max_left, " (realtime)"); 04688 if (mem->paused) 04689 ast_build_string(&max, &max_left, " (paused)"); 04690 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status)); 04691 if (mem->calls) { 04692 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)", 04693 mem->calls, (long) (time(NULL) - mem->lastcall)); 04694 } else 04695 ast_build_string(&max, &max_left, " has taken no calls yet"); 04696 if (s) 04697 astman_append(s, " %s%s%s", mem->membername, max_buf, term); 04698 else 04699 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term); 04700 ao2_ref(mem, -1); 04701 } 04702 } else if (s) 04703 astman_append(s, " No Members%s", term); 04704 else 04705 ast_cli(fd, " No Members%s", term); 04706 if (q->head) { 04707 pos = 1; 04708 if (s) 04709 astman_append(s, " Callers: %s", term); 04710 else 04711 ast_cli(fd, " Callers: %s", term); 04712 for (qe = q->head; qe; qe = qe->next) { 04713 if (s) 04714 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", 04715 pos++, qe->chan->name, (long) (now - qe->start) / 60, 04716 (long) (now - qe->start) % 60, qe->prio, term); 04717 else 04718 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, 04719 qe->chan->name, (long) (now - qe->start) / 60, 04720 (long) (now - qe->start) % 60, qe->prio, term); 04721 } 04722 } else if (s) 04723 astman_append(s, " No Callers%s", term); 04724 else 04725 ast_cli(fd, " No Callers%s", term); 04726 if (s) 04727 astman_append(s, "%s", term); 04728 else 04729 ast_cli(fd, "%s", term); 04730 ast_mutex_unlock(&q->lock); 04731 if (queue_show) 04732 break; 04733 } 04734 AST_LIST_UNLOCK(&queues); 04735 return RESULT_SUCCESS; 04736 }
static void __reg_module | ( | void | ) | [static] |
Definition at line 5268 of file app_queue.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 5268 of file app_queue.c.
static int add_to_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 881 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, member_interface::list, LOG_DEBUG, and option_debug.
Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().
00882 { 00883 struct member_interface *curint; 00884 00885 AST_LIST_LOCK(&interfaces); 00886 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00887 if (!strcasecmp(curint->interface, interface)) 00888 break; 00889 } 00890 00891 if (curint) { 00892 AST_LIST_UNLOCK(&interfaces); 00893 return 0; 00894 } 00895 00896 if (option_debug) 00897 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface); 00898 00899 if ((curint = ast_calloc(1, sizeof(*curint)))) { 00900 ast_copy_string(curint->interface, interface, sizeof(curint->interface)); 00901 AST_LIST_INSERT_HEAD(&interfaces, curint, list); 00902 } 00903 AST_LIST_UNLOCK(&interfaces); 00904 00905 return 0; 00906 }
static int add_to_queue | ( | const char * | queuename, | |
const char * | interface, | |||
const char * | membername, | |||
int | penalty, | |||
int | paused, | |||
int | dump | |||
) | [static] |
Definition at line 3405 of file app_queue.c.
References add_to_interfaces(), ao2_ref(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), call_queue::lock, 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, and member::status.
Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().
03406 { 03407 struct call_queue *q; 03408 struct member *new_member, *old_member; 03409 int res = RES_NOSUCHQUEUE; 03410 03411 /* \note Ensure the appropriate realtime queue is loaded. Note that this 03412 * short-circuits if the queue is already in memory. */ 03413 if (!(q = load_realtime_queue(queuename))) 03414 return res; 03415 03416 AST_LIST_LOCK(&queues); 03417 03418 ast_mutex_lock(&q->lock); 03419 if ((old_member = interface_exists(q, interface)) == NULL) { 03420 add_to_interfaces(interface); 03421 if ((new_member = create_queue_member(interface, membername, penalty, paused))) { 03422 new_member->dynamic = 1; 03423 ao2_link(q->members, new_member); 03424 q->membercount++; 03425 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 03426 "Queue: %s\r\n" 03427 "Location: %s\r\n" 03428 "MemberName: %s\r\n" 03429 "Membership: %s\r\n" 03430 "Penalty: %d\r\n" 03431 "CallsTaken: %d\r\n" 03432 "LastCall: %d\r\n" 03433 "Status: %d\r\n" 03434 "Paused: %d\r\n", 03435 q->name, new_member->interface, new_member->membername, 03436 "dynamic", 03437 new_member->penalty, new_member->calls, (int) new_member->lastcall, 03438 new_member->status, new_member->paused); 03439 03440 ao2_ref(new_member, -1); 03441 new_member = NULL; 03442 03443 if (dump) 03444 dump_queue_members(q); 03445 03446 res = RES_OKAY; 03447 } else { 03448 res = RES_OUTOFMEMORY; 03449 } 03450 } else { 03451 ao2_ref(old_member, -1); 03452 res = RES_EXISTS; 03453 } 03454 ast_mutex_unlock(&q->lock); 03455 AST_LIST_UNLOCK(&queues); 03456 03457 return res; 03458 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static] |
Definition at line 782 of file app_queue.c.
References ast_calloc, ast_copy_string(), and ast_mutex_init().
Referenced by find_queue_by_name_rt(), and reload_queues().
00783 { 00784 struct call_queue *q; 00785 00786 if ((q = ast_calloc(1, sizeof(*q)))) { 00787 ast_mutex_init(&q->lock); 00788 ast_copy_string(q->name, queuename, sizeof(q->name)); 00789 } 00790 return q; 00791 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3784 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_module_user::chan, ast_channel::context, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and ast_channel::uniqueid.
Referenced by load_module().
03785 { 03786 int res=-1; 03787 struct ast_module_user *lu; 03788 char *parse, *temppos = NULL; 03789 int priority_jump = 0; 03790 AST_DECLARE_APP_ARGS(args, 03791 AST_APP_ARG(queuename); 03792 AST_APP_ARG(interface); 03793 AST_APP_ARG(penalty); 03794 AST_APP_ARG(options); 03795 AST_APP_ARG(membername); 03796 ); 03797 int penalty = 0; 03798 03799 if (ast_strlen_zero(data)) { 03800 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n"); 03801 return -1; 03802 } 03803 03804 parse = ast_strdupa(data); 03805 03806 AST_STANDARD_APP_ARGS(args, parse); 03807 03808 lu = ast_module_user_add(chan); 03809 03810 if (ast_strlen_zero(args.interface)) { 03811 args.interface = ast_strdupa(chan->name); 03812 temppos = strrchr(args.interface, '-'); 03813 if (temppos) 03814 *temppos = '\0'; 03815 } 03816 03817 if (!ast_strlen_zero(args.penalty)) { 03818 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) { 03819 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 03820 penalty = 0; 03821 } 03822 } 03823 03824 if (args.options) { 03825 if (strchr(args.options, 'j')) 03826 priority_jump = 1; 03827 } 03828 03829 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) { 03830 case RES_OKAY: 03831 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 03832 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 03833 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 03834 res = 0; 03835 break; 03836 case RES_EXISTS: 03837 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 03838 if (priority_jump || ast_opt_priority_jumping) 03839 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 03840 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 03841 res = 0; 03842 break; 03843 case RES_NOSUCHQUEUE: 03844 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 03845 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 03846 res = 0; 03847 break; 03848 case RES_OUTOFMEMORY: 03849 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 03850 break; 03851 } 03852 03853 ast_module_user_remove(lu); 03854 03855 return res; 03856 }
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 2669 of file app_queue.c.
References ast_channel_datastore_find(), and queue_transfer_info.
Referenced by try_calling().
02670 { 02671 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 02672 }
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 2547 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, member::ringcount, call_queue::ringlimit, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
Referenced by try_calling().
02548 { 02549 if (qe->max_penalty && (mem->penalty > qe->max_penalty)) 02550 return -1; 02551 02552 switch (q->strategy) { 02553 case QUEUE_STRATEGY_RINGALL: 02554 /* Everyone equal, except for penalty */ 02555 tmp->metric = mem->penalty * 1000000; 02556 break; 02557 case QUEUE_STRATEGY_ROUNDROBIN: 02558 if (!pos) { 02559 if (!q->wrapped) { 02560 /* No more channels, start over */ 02561 q->rrpos = 0; 02562 } else { 02563 /* Prioritize next entry */ 02564 q->rrpos++; 02565 } 02566 q->wrapped = 0; 02567 } 02568 /* Fall through */ 02569 case QUEUE_STRATEGY_RRMEMORY: 02570 if (pos < q->rrpos) { 02571 tmp->metric = 1000 + pos; 02572 } else { 02573 if (pos > q->rrpos) 02574 /* Indicate there is another priority */ 02575 q->wrapped = 1; 02576 tmp->metric = pos; 02577 } 02578 tmp->metric += mem->penalty * 1000000; 02579 break; 02580 case QUEUE_STRATEGY_RANDOM: 02581 tmp->metric = ast_random() % 1000; 02582 tmp->metric += mem->penalty * 1000000; 02583 break; 02584 case QUEUE_STRATEGY_FEWESTCALLS: 02585 tmp->metric = mem->calls; 02586 tmp->metric += mem->penalty * 1000000; 02587 break; 02588 case QUEUE_STRATEGY_LEASTRECENT: 02589 if (!mem->lastcall) 02590 tmp->metric = 0; 02591 else 02592 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 02593 tmp->metric += mem->penalty * 1000000; 02594 break; 02595 default: 02596 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 02597 break; 02598 } 02599 if (q->ringlimit && (mem->ringcount >= q->ringlimit)) { 02600 tmp->metric += (mem->ringcount / q->ringlimit) * 10000000; 02601 } 02602 if (option_debug) 02603 ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 02604 tmp->metric, mem->interface, mem->ringcount, q->ringlimit); 02605 return 0; 02606 }
static void clear_and_free_interfaces | ( | void | ) | [static] |
Definition at line 955 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, free, and member_interface::list.
Referenced by unload_module().
00956 { 00957 struct member_interface *curint; 00958 00959 AST_LIST_LOCK(&interfaces); 00960 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list))) 00961 free(curint); 00962 AST_LIST_UNLOCK(&interfaces); 00963 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 872 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().
00873 { 00874 q->holdtime = 0; 00875 q->callscompleted = 0; 00876 q->callsabandoned = 0; 00877 q->callscompletedinsl = 0; 00878 q->wrapuptime = 0; 00879 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 1736 of file app_queue.c.
References ao2_find(), ao2_ref(), AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::count, member::interface, member_interface::list, call_queue::lock, LOG_DEBUG, call_queue::members, call_queue::name, and call_queue::weight.
Referenced by ring_entry().
01737 { 01738 struct call_queue *q; 01739 struct member *mem; 01740 int found = 0; 01741 01742 /* &qlock and &rq->lock already set by try_calling() 01743 * to solve deadlock */ 01744 AST_LIST_TRAVERSE(&queues, q, list) { 01745 if (q == rq) /* don't check myself, could deadlock */ 01746 continue; 01747 ast_mutex_lock(&q->lock); 01748 if (q->count && q->members) { 01749 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 01750 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 01751 if (q->weight > rq->weight) { 01752 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); 01753 found = 1; 01754 } 01755 ao2_ref(mem, -1); 01756 } 01757 } 01758 ast_mutex_unlock(&q->lock); 01759 if (found) 01760 break; 01761 } 01762 return found; 01763 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4743 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, call_queue::list, and call_queue::name.
Referenced by complete_queue_add_member(), complete_queue_remove_member(), and complete_queue_show().
04744 { 04745 struct call_queue *q; 04746 char *ret = NULL; 04747 int which = 0; 04748 int wordlen = strlen(word); 04749 04750 AST_LIST_LOCK(&queues); 04751 AST_LIST_TRAVERSE(&queues, q, list) { 04752 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 04753 ret = ast_strdup(q->name); 04754 break; 04755 } 04756 } 04757 AST_LIST_UNLOCK(&queues); 04758 04759 return ret; 04760 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5038 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
05039 { 05040 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 05041 switch (pos) { 05042 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 05043 return NULL; 05044 case 4: /* only one possible match, "to" */ 05045 return state == 0 ? ast_strdup("to") : NULL; 05046 case 5: /* <queue> */ 05047 return complete_queue(line, word, pos, state); 05048 case 6: /* only one possible match, "penalty" */ 05049 return state == 0 ? ast_strdup("penalty") : NULL; 05050 case 7: 05051 if (state < 100) { /* 0-99 */ 05052 char *num; 05053 if ((num = ast_malloc(3))) { 05054 sprintf(num, "%d", state); 05055 } 05056 return num; 05057 } else { 05058 return NULL; 05059 } 05060 case 8: /* only one possible match, "as" */ 05061 return state == 0 ? ast_strdup("as") : NULL; 05062 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 05063 return NULL; 05064 default: 05065 return NULL; 05066 } 05067 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5104 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_mutex_lock(), ast_mutex_unlock(), ast_strdup, complete_queue(), member::interface, call_queue::lock, and call_queue::members.
05105 { 05106 int which = 0; 05107 struct call_queue *q; 05108 struct member *m; 05109 struct ao2_iterator mem_iter; 05110 05111 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 05112 if (pos > 5 || pos < 3) 05113 return NULL; 05114 if (pos == 4) /* only one possible match, 'from' */ 05115 return state == 0 ? ast_strdup("from") : NULL; 05116 05117 if (pos == 5) /* No need to duplicate code */ 05118 return complete_queue(line, word, pos, state); 05119 05120 /* here is the case for 3, <member> */ 05121 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ 05122 AST_LIST_TRAVERSE(&queues, q, list) { 05123 ast_mutex_lock(&q->lock); 05124 mem_iter = ao2_iterator_init(q->members, 0); 05125 while ((m = ao2_iterator_next(&mem_iter))) { 05126 if (++which > state) { 05127 char *tmp; 05128 ast_mutex_unlock(&q->lock); 05129 tmp = ast_strdup(m->interface); 05130 ao2_ref(m, -1); 05131 return tmp; 05132 } 05133 ao2_ref(m, -1); 05134 } 05135 ast_mutex_unlock(&q->lock); 05136 } 05137 } 05138 05139 return NULL; 05140 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4762 of file app_queue.c.
References complete_queue().
04763 { 04764 if (pos == 2) 04765 return complete_queue(line, word, pos, state); 04766 return NULL; 04767 }
static int compress_char | ( | const char | c | ) | [static] |
static struct member* create_queue_member | ( | const char * | interface, | |
const char * | membername, | |||
int | penalty, | |||
int | paused | |||
) | [static] |
allocate space for new queue member and set fields based on parameters passed
Definition at line 762 of file app_queue.c.
References ao2_alloc(), ast_copy_string(), ast_device_state(), ast_log(), ast_strlen_zero(), LOG_WARNING, and member::penalty.
Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().
00763 { 00764 struct member *cur; 00765 00766 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 00767 cur->penalty = penalty; 00768 cur->paused = paused; 00769 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 00770 if (!ast_strlen_zero(membername)) 00771 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 00772 else 00773 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 00774 if (!strchr(cur->interface, '/')) 00775 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 00776 cur->status = ast_device_state(interface); 00777 } 00778 00779 return cur; 00780 }
static void destroy_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 1182 of file app_queue.c.
References ao2_ref(), ast_mutex_destroy(), free, free_members(), call_queue::lock, and call_queue::members.
Referenced by find_queue_by_name_rt(), and leave_queue().
01183 { 01184 free_members(q, 1); 01185 ast_mutex_destroy(&q->lock); 01186 ao2_ref(q->members, -1); 01187 free(q); 01188 }
static void* device_state_thread | ( | void * | data | ) | [static] |
Consumer of the statechange queue.
Definition at line 710 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().
00711 { 00712 struct statechange *sc = NULL; 00713 00714 while (!device_state.stop) { 00715 ast_mutex_lock(&device_state.lock); 00716 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) { 00717 ast_cond_wait(&device_state.cond, &device_state.lock); 00718 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry); 00719 } 00720 ast_mutex_unlock(&device_state.lock); 00721 00722 /* Check to see if we were woken up to see the request to stop */ 00723 if (device_state.stop) 00724 break; 00725 00726 if (!sc) 00727 continue; 00728 00729 handle_statechange(sc); 00730 00731 free(sc); 00732 sc = NULL; 00733 } 00734 00735 if (sc) 00736 free(sc); 00737 00738 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) 00739 free(sc); 00740 00741 return NULL; 00742 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 1766 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry().
01767 { 01768 o->stillgoing = 0; 01769 ast_hangup(o->chan); 01770 o->chan = NULL; 01771 }
static void dump_queue_members | ( | struct call_queue * | pm_queue | ) | [static] |
Definition at line 3312 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_db_del(), ast_db_put(), ast_log(), member::dynamic, member::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, and PM_MAX_LEN.
Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().
03313 { 03314 struct member *cur_member; 03315 char value[PM_MAX_LEN]; 03316 int value_len = 0; 03317 int res; 03318 struct ao2_iterator mem_iter; 03319 03320 memset(value, 0, sizeof(value)); 03321 03322 if (!pm_queue) 03323 return; 03324 03325 mem_iter = ao2_iterator_init(pm_queue->members, 0); 03326 while ((cur_member = ao2_iterator_next(&mem_iter))) { 03327 if (!cur_member->dynamic) { 03328 ao2_ref(cur_member, -1); 03329 continue; 03330 } 03331 03332 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s", 03333 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername); 03334 03335 ao2_ref(cur_member, -1); 03336 03337 if (res != strlen(value + value_len)) { 03338 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 03339 break; 03340 } 03341 value_len += res; 03342 } 03343 03344 if (value_len && !cur_member) { 03345 if (ast_db_put(pm_family, pm_queue->name, value)) 03346 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 03347 } else 03348 /* Delete the entry if the queue is empty or there is an error */ 03349 ast_db_del(pm_family, pm_queue->name); 03350 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static] |
find the entry with the best metric, or NULL
Definition at line 1957 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
01958 { 01959 struct callattempt *best = NULL, *cur; 01960 01961 for (cur = outgoing; cur; cur = cur->q_next) { 01962 if (cur->stillgoing && /* Not already done */ 01963 !cur->chan && /* Isn't already going */ 01964 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 01965 best = cur; 01966 } 01967 } 01968 01969 return best; 01970 }
static struct call_queue* find_queue_by_name_rt | ( | const char * | queuename, | |
struct ast_variable * | queue_vars, | |||
struct ast_config * | member_config | |||
) | [static] |
Reload a single queue via realtime.
Definition at line 1193 of file app_queue.c.
References alloc_queue(), ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), clear_queue(), call_queue::dead, destroy_queue(), init_queue(), member_interface::interface, member_interface::list, call_queue::lock, LOG_DEBUG, LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), call_queue::realtime, and ast_variable::value.
Referenced by load_realtime_queue().
01194 { 01195 struct ast_variable *v; 01196 struct call_queue *q; 01197 struct member *m; 01198 struct ao2_iterator mem_iter; 01199 char *interface = NULL; 01200 char *tmp, *tmp_name; 01201 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 01202 01203 /* Find the queue in the in-core list (we will create a new one if not found). */ 01204 AST_LIST_TRAVERSE(&queues, q, list) { 01205 if (!strcasecmp(q->name, queuename)) 01206 break; 01207 } 01208 01209 /* Static queues override realtime. */ 01210 if (q) { 01211 ast_mutex_lock(&q->lock); 01212 if (!q->realtime) { 01213 if (q->dead) { 01214 ast_mutex_unlock(&q->lock); 01215 return NULL; 01216 } else { 01217 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 01218 ast_mutex_unlock(&q->lock); 01219 return q; 01220 } 01221 } 01222 } else if (!member_config) 01223 /* Not found in the list, and it's not realtime ... */ 01224 return NULL; 01225 01226 /* Check if queue is defined in realtime. */ 01227 if (!queue_vars) { 01228 /* Delete queue from in-core list if it has been deleted in realtime. */ 01229 if (q) { 01230 /*! \note Hmm, can't seem to distinguish a DB failure from a not 01231 found condition... So we might delete an in-core queue 01232 in case of DB failure. */ 01233 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename); 01234 01235 q->dead = 1; 01236 /* Delete if unused (else will be deleted when last caller leaves). */ 01237 if (!q->count) { 01238 /* Delete. */ 01239 AST_LIST_REMOVE(&queues, q, list); 01240 ast_mutex_unlock(&q->lock); 01241 destroy_queue(q); 01242 } else 01243 ast_mutex_unlock(&q->lock); 01244 } 01245 return NULL; 01246 } 01247 01248 /* Create a new queue if an in-core entry does not exist yet. */ 01249 if (!q) { 01250 if (!(q = alloc_queue(queuename))) 01251 return NULL; 01252 ast_mutex_lock(&q->lock); 01253 clear_queue(q); 01254 q->realtime = 1; 01255 AST_LIST_INSERT_HEAD(&queues, q, list); 01256 } 01257 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 01258 01259 memset(tmpbuf, 0, sizeof(tmpbuf)); 01260 for (v = queue_vars; v; v = v->next) { 01261 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 01262 if ((tmp = strchr(v->name, '_'))) { 01263 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 01264 tmp_name = tmpbuf; 01265 tmp = tmp_name; 01266 while ((tmp = strchr(tmp, '_'))) 01267 *tmp++ = '-'; 01268 } else 01269 tmp_name = v->name; 01270 01271 if (!ast_strlen_zero(v->value)) { 01272 /* Don't want to try to set the option if the value is empty */ 01273 queue_set_param(q, tmp_name, v->value, -1, 0); 01274 } 01275 } 01276 01277 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 01278 rr_dep_warning(); 01279 01280 /* Temporarily set realtime members dead so we can detect deleted ones. 01281 * Also set the membercount correctly for realtime*/ 01282 mem_iter = ao2_iterator_init(q->members, 0); 01283 while ((m = ao2_iterator_next(&mem_iter))) { 01284 q->membercount++; 01285 if (m->realtime) 01286 m->dead = 1; 01287 ao2_ref(m, -1); 01288 } 01289 01290 while ((interface = ast_category_browse(member_config, interface))) { 01291 rt_handle_member_record(q, interface, 01292 ast_variable_retrieve(member_config, interface, "membername"), 01293 ast_variable_retrieve(member_config, interface, "penalty"), 01294 ast_variable_retrieve(member_config, interface, "paused")); 01295 } 01296 01297 /* Delete all realtime members that have been deleted in DB. */ 01298 mem_iter = ao2_iterator_init(q->members, 0); 01299 while ((m = ao2_iterator_next(&mem_iter))) { 01300 if (m->dead) { 01301 ao2_unlink(q->members, m); 01302 ast_mutex_unlock(&q->lock); 01303 remove_from_interfaces(m->interface); 01304 ast_mutex_lock(&q->lock); 01305 q->membercount--; 01306 } 01307 ao2_ref(m, -1); 01308 } 01309 01310 ast_mutex_unlock(&q->lock); 01311 01312 return q; 01313 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Definition at line 1166 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), member::dynamic, member::interface, call_queue::membercount, call_queue::members, and remove_from_interfaces().
Referenced by destroy_queue().
01167 { 01168 /* Free non-dynamic members */ 01169 struct member *cur; 01170 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01171 01172 while ((cur = ao2_iterator_next(&mem_iter))) { 01173 if (all || !cur->dynamic) { 01174 ao2_unlink(q->members, cur); 01175 remove_from_interfaces(cur->interface); 01176 q->membercount--; 01177 } 01178 ao2_ref(cur, -1); 01179 } 01180 }
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 551 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_mutex_lock(), ast_mutex_unlock(), call_queue::lock, 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().
00552 { 00553 struct member *member; 00554 struct ao2_iterator mem_iter; 00555 enum queue_member_status result = QUEUE_NO_MEMBERS; 00556 00557 ast_mutex_lock(&q->lock); 00558 mem_iter = ao2_iterator_init(q->members, 0); 00559 while ((member = ao2_iterator_next(&mem_iter))) { 00560 if (max_penalty && (member->penalty > max_penalty)) { 00561 ao2_ref(member, -1); 00562 continue; 00563 } 00564 00565 if (member->paused) { 00566 ao2_ref(member, -1); 00567 continue; 00568 } 00569 00570 switch (member->status) { 00571 case AST_DEVICE_INVALID: 00572 /* nothing to do */ 00573 ao2_ref(member, -1); 00574 break; 00575 case AST_DEVICE_UNAVAILABLE: 00576 result = QUEUE_NO_REACHABLE_MEMBERS; 00577 ao2_ref(member, -1); 00578 break; 00579 default: 00580 ast_mutex_unlock(&q->lock); 00581 ao2_ref(member, -1); 00582 return QUEUE_NORMAL; 00583 } 00584 } 00585 00586 ast_mutex_unlock(&q->lock); 00587 return result; 00588 }
static int handle_queue_add_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 4984 of file app_queue.c.
References add_to_queue(), ast_cli(), ast_queue_log(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.
04985 { 04986 char *queuename, *interface, *membername = NULL; 04987 int penalty; 04988 04989 if ((argc != 6) && (argc != 8) && (argc != 10)) { 04990 return RESULT_SHOWUSAGE; 04991 } else if (strcmp(argv[4], "to")) { 04992 return RESULT_SHOWUSAGE; 04993 } else if ((argc == 8) && strcmp(argv[6], "penalty")) { 04994 return RESULT_SHOWUSAGE; 04995 } else if ((argc == 10) && strcmp(argv[8], "as")) { 04996 return RESULT_SHOWUSAGE; 04997 } 04998 04999 queuename = argv[5]; 05000 interface = argv[3]; 05001 if (argc >= 8) { 05002 if (sscanf(argv[7], "%d", &penalty) == 1) { 05003 if (penalty < 0) { 05004 ast_cli(fd, "Penalty must be >= 0\n"); 05005 penalty = 0; 05006 } 05007 } else { 05008 ast_cli(fd, "Penalty must be an integer >= 0\n"); 05009 penalty = 0; 05010 } 05011 } else { 05012 penalty = 0; 05013 } 05014 05015 if (argc >= 10) { 05016 membername = argv[9]; 05017 } 05018 05019 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) { 05020 case RES_OKAY: 05021 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 05022 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 05023 return RESULT_SUCCESS; 05024 case RES_EXISTS: 05025 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 05026 return RESULT_FAILURE; 05027 case RES_NOSUCHQUEUE: 05028 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 05029 return RESULT_FAILURE; 05030 case RES_OUTOFMEMORY: 05031 ast_cli(fd, "Out of memory\n"); 05032 return RESULT_FAILURE; 05033 default: 05034 return RESULT_FAILURE; 05035 } 05036 }
static int handle_queue_remove_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 5069 of file app_queue.c.
References ast_cli(), ast_queue_log(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.
05070 { 05071 char *queuename, *interface; 05072 05073 if (argc != 6) { 05074 return RESULT_SHOWUSAGE; 05075 } else if (strcmp(argv[4], "from")) { 05076 return RESULT_SHOWUSAGE; 05077 } 05078 05079 queuename = argv[5]; 05080 interface = argv[3]; 05081 05082 switch (remove_from_queue(queuename, interface)) { 05083 case RES_OKAY: 05084 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 05085 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 05086 return RESULT_SUCCESS; 05087 case RES_EXISTS: 05088 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 05089 return RESULT_FAILURE; 05090 case RES_NOSUCHQUEUE: 05091 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 05092 return RESULT_FAILURE; 05093 case RES_OUTOFMEMORY: 05094 ast_cli(fd, "Out of memory\n"); 05095 return RESULT_FAILURE; 05096 case RES_NOT_DYNAMIC: 05097 ast_cli(fd, "Member not dynamic\n"); 05098 return RESULT_FAILURE; 05099 default: 05100 return RESULT_FAILURE; 05101 } 05102 }
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 649 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdupa, statechange::dev, devstate2str(), member_interface::interface, member_interface::list, LOG_DEBUG, option_debug, statechange::state, and update_status().
Referenced by device_state_thread().
00650 { 00651 struct member_interface *curint; 00652 char *loc; 00653 char *technology; 00654 00655 technology = ast_strdupa(sc->dev); 00656 loc = strchr(technology, '/'); 00657 if (loc) { 00658 *loc++ = '\0'; 00659 } else { 00660 return NULL; 00661 } 00662 00663 AST_LIST_LOCK(&interfaces); 00664 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00665 char *interface; 00666 char *slash_pos; 00667 interface = ast_strdupa(curint->interface); 00668 if ((slash_pos = strchr(interface, '/'))) 00669 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00670 *slash_pos = '\0'; 00671 00672 if (!strcasecmp(interface, sc->dev)) 00673 break; 00674 } 00675 AST_LIST_UNLOCK(&interfaces); 00676 00677 if (!curint) { 00678 if (option_debug > 2) 00679 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)); 00680 return NULL; 00681 } 00682 00683 if (option_debug) 00684 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state)); 00685 00686 update_status(sc->dev, sc->state); 00687 00688 return NULL; 00689 }
static void hangupcalls | ( | struct callattempt * | outgoing, | |
struct ast_channel * | exception | |||
) | [static] |
Definition at line 1717 of file app_queue.c.
References ao2_ref(), ast_hangup(), callattempt::chan, free, callattempt::member, and callattempt::q_next.
Referenced by try_calling().
01718 { 01719 struct callattempt *oo; 01720 01721 while (outgoing) { 01722 /* Hangup any existing lines we have open */ 01723 if (outgoing->chan && (outgoing->chan != exception)) 01724 ast_hangup(outgoing->chan); 01725 oo = outgoing; 01726 outgoing = outgoing->q_next; 01727 if (oo->member) 01728 ao2_ref(oo->member, -1); 01729 free(oo); 01730 } 01731 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 821 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, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_queues().
00822 { 00823 int i; 00824 00825 q->dead = 0; 00826 q->retry = DEFAULT_RETRY; 00827 q->timeout = -1; 00828 q->maxlen = 0; 00829 q->ringlimit = 0; 00830 q->announcefrequency = 0; 00831 q->announceholdtime = 0; 00832 q->roundingseconds = 0; /* Default - don't announce seconds */ 00833 q->servicelevel = 0; 00834 q->ringinuse = 1; 00835 q->setinterfacevar = 0; 00836 q->autofill = autofill_default; 00837 q->montype = montype_default; 00838 q->moh[0] = '\0'; 00839 q->announce[0] = '\0'; 00840 q->context[0] = '\0'; 00841 q->monfmt[0] = '\0'; 00842 q->periodicannouncefrequency = 0; 00843 q->reportholdtime = 0; 00844 q->monjoin = 0; 00845 q->wrapuptime = 0; 00846 q->joinempty = 0; 00847 q->leavewhenempty = 0; 00848 q->memberdelay = 0; 00849 q->maskmemberstatus = 0; 00850 q->eventwhencalled = 0; 00851 q->weight = 0; 00852 q->timeoutrestart = 0; 00853 if (!q->members) 00854 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 00855 q->membercount = 0; 00856 q->found = 1; 00857 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); 00858 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare)); 00859 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls)); 00860 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime)); 00861 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes)); 00862 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds)); 00863 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks)); 00864 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan)); 00865 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold)); 00866 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0])); 00867 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 00868 q->sound_periodicannounce[i][0]='\0'; 00869 } 00870 }
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 520 of file app_queue.c.
References call_queue::head, queue_ent::next, and queue_ent::parent.
Referenced by join_queue().
00521 { 00522 struct queue_ent *cur; 00523 00524 if (!q || !new) 00525 return; 00526 if (prev) { 00527 cur = prev->next; 00528 prev->next = new; 00529 } else { 00530 cur = q->head; 00531 q->head = new; 00532 } 00533 new->next = cur; 00534 new->parent = q; 00535 new->pos = ++(*pos); 00536 new->opos = *pos; 00537 }
static char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 495 of file app_queue.c.
References name, and strategies.
Referenced by __queues_show().
00496 { 00497 int x; 00498 00499 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) { 00500 if (strategy == strategies[x].strategy) 00501 return strategies[x].name; 00502 } 00503 00504 return "<unknown>"; 00505 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static] |
Definition at line 3288 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), member::interface, and call_queue::members.
Referenced by add_to_queue(), and set_member_paused().
03289 { 03290 struct member *mem; 03291 struct ao2_iterator mem_iter; 03292 03293 if (!q) 03294 return NULL; 03295 03296 mem_iter = ao2_iterator_init(q->members, 0); 03297 while ((mem = ao2_iterator_next(&mem_iter))) { 03298 if (!strcasecmp(interface, mem->interface)) 03299 return mem; 03300 ao2_ref(mem, -1); 03301 } 03302 03303 return NULL; 03304 }
static int interface_exists_global | ( | const char * | interface | ) | [static] |
Definition at line 908 of file app_queue.c.
References ao2_find(), ao2_ref(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::interface, member_interface::list, call_queue::lock, and call_queue::members.
Referenced by remove_from_interfaces().
00909 { 00910 struct call_queue *q; 00911 struct member *mem, tmpmem; 00912 int ret = 0; 00913 00914 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 00915 00916 AST_LIST_LOCK(&queues); 00917 AST_LIST_TRAVERSE(&queues, q, list) { 00918 ast_mutex_lock(&q->lock); 00919 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 00920 ao2_ref(mem, -1); 00921 ret = 1; 00922 } 00923 ast_mutex_unlock(&q->lock); 00924 if (ret) 00925 break; 00926 } 00927 AST_LIST_UNLOCK(&queues); 00928 00929 return ret; 00930 }
static int is_our_turn | ( | struct queue_ent * | qe | ) | [static] |
Check if we should start attempting to call queue members.
The behavior of this function is dependent first on whether autofill is enabled and second on whether the ring strategy is ringall. If autofill is not enabled, then return true if we're the head of the queue. If autofill is enabled, then we count the available members and see if the number of available members is enough that given our position in the queue, we would theoretically be able to connect to one of those available members
Definition at line 2369 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::autofill, queue_ent::chan, call_queue::head, call_queue::lock, LOG_DEBUG, call_queue::members, ast_channel::name, queue_ent::next, option_debug, queue_ent::parent, member::paused, queue_ent::pending, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.
Referenced by queue_exec(), and wait_our_turn().
02370 { 02371 struct queue_ent *ch; 02372 struct member *cur; 02373 int avl = 0; 02374 int idx = 0; 02375 int res; 02376 02377 if (!qe->parent->autofill) { 02378 /* Atomically read the parent head -- does not need a lock */ 02379 ch = qe->parent->head; 02380 /* If we are now at the top of the head, break out */ 02381 if (ch == qe) { 02382 if (option_debug) 02383 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02384 res = 1; 02385 } else { 02386 if (option_debug) 02387 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02388 res = 0; 02389 } 02390 02391 } else { 02392 /* This needs a lock. How many members are available to be served? */ 02393 ast_mutex_lock(&qe->parent->lock); 02394 02395 ch = qe->parent->head; 02396 02397 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 02398 if (option_debug) 02399 ast_log(LOG_DEBUG, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n"); 02400 avl = 1; 02401 } else { 02402 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0); 02403 while ((cur = ao2_iterator_next(&mem_iter))) { 02404 switch (cur->status) { 02405 case AST_DEVICE_INUSE: 02406 if (!qe->parent->ringinuse) 02407 break; 02408 /* else fall through */ 02409 case AST_DEVICE_NOT_INUSE: 02410 case AST_DEVICE_UNKNOWN: 02411 if (!cur->paused) 02412 avl++; 02413 break; 02414 } 02415 ao2_ref(cur, -1); 02416 } 02417 } 02418 02419 if (option_debug) 02420 ast_log(LOG_DEBUG, "There are %d available members.\n", avl); 02421 02422 while ((idx < avl) && (ch) && (ch != qe)) { 02423 if (!ch->pending) 02424 idx++; 02425 ch = ch->next; 02426 } 02427 02428 /* If the queue entry is within avl [the number of available members] calls from the top ... */ 02429 if (ch && idx < avl) { 02430 if (option_debug) 02431 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02432 res = 1; 02433 } else { 02434 if (option_debug) 02435 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02436 res = 0; 02437 } 02438 02439 ast_mutex_unlock(&qe->parent->lock); 02440 } 02441 02442 return res; 02443 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason | |||
) | [static] |
Definition at line 1433 of file app_queue.c.
References queue_ent::announce, ast_copy_string(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), call_queue::lock, LOG_DEBUG, manager_event(), queue_ent::max_penalty, call_queue::maxlen, queue_ent::moh, ast_channel::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, S_OR, and ast_channel::uniqueid.
Referenced by queue_exec().
01434 { 01435 struct call_queue *q; 01436 struct queue_ent *cur, *prev = NULL; 01437 int res = -1; 01438 int pos = 0; 01439 int inserted = 0; 01440 enum queue_member_status stat; 01441 01442 if (!(q = load_realtime_queue(queuename))) 01443 return res; 01444 01445 AST_LIST_LOCK(&queues); 01446 ast_mutex_lock(&q->lock); 01447 01448 /* This is our one */ 01449 stat = get_member_status(q, qe->max_penalty); 01450 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS)) 01451 *reason = QUEUE_JOINEMPTY; 01452 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS)) 01453 *reason = QUEUE_JOINUNAVAIL; 01454 else if (q->maxlen && (q->count >= q->maxlen)) 01455 *reason = QUEUE_FULL; 01456 else { 01457 /* There's space for us, put us at the right position inside 01458 * the queue. 01459 * Take into account the priority of the calling user */ 01460 inserted = 0; 01461 prev = NULL; 01462 cur = q->head; 01463 while (cur) { 01464 /* We have higher priority than the current user, enter 01465 * before him, after all the other users with priority 01466 * higher or equal to our priority. */ 01467 if ((!inserted) && (qe->prio > cur->prio)) { 01468 insert_entry(q, prev, qe, &pos); 01469 inserted = 1; 01470 } 01471 cur->pos = ++pos; 01472 prev = cur; 01473 cur = cur->next; 01474 } 01475 /* No luck, join at the end of the queue */ 01476 if (!inserted) 01477 insert_entry(q, prev, qe, &pos); 01478 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 01479 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 01480 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 01481 q->count++; 01482 res = 0; 01483 manager_event(EVENT_FLAG_CALL, "Join", 01484 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n", 01485 qe->chan->name, 01486 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */ 01487 S_OR(qe->chan->cid.cid_name, "unknown"), 01488 q->name, qe->pos, q->count, qe->chan->uniqueid ); 01489 if (option_debug) 01490 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 01491 } 01492 ast_mutex_unlock(&q->lock); 01493 AST_LIST_UNLOCK(&queues); 01494 01495 return res; 01496 }
static void leave_queue | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1673 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, call_queue::count, call_queue::dead, destroy_queue(), EVENT_FLAG_CALL, call_queue::head, member_interface::list, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::name, ast_channel::name, queue_ent::next, option_debug, queue_ent::parent, queue_ent::pos, and ast_channel::uniqueid.
Referenced by queue_exec(), try_calling(), and wait_our_turn().
01674 { 01675 struct call_queue *q; 01676 struct queue_ent *cur, *prev = NULL; 01677 int pos = 0; 01678 01679 if (!(q = qe->parent)) 01680 return; 01681 ast_mutex_lock(&q->lock); 01682 01683 prev = NULL; 01684 for (cur = q->head; cur; cur = cur->next) { 01685 if (cur == qe) { 01686 q->count--; 01687 01688 /* Take us out of the queue */ 01689 manager_event(EVENT_FLAG_CALL, "Leave", 01690 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", 01691 qe->chan->name, q->name, q->count, qe->chan->uniqueid); 01692 if (option_debug) 01693 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 01694 /* Take us out of the queue */ 01695 if (prev) 01696 prev->next = cur->next; 01697 else 01698 q->head = cur->next; 01699 } else { 01700 /* Renumber the people after us in the queue based on a new count */ 01701 cur->pos = ++pos; 01702 prev = cur; 01703 } 01704 } 01705 ast_mutex_unlock(&q->lock); 01706 01707 if (q->dead && !q->count) { 01708 /* It's dead and nobody is in it, so kill it */ 01709 AST_LIST_LOCK(&queues); 01710 AST_LIST_REMOVE(&queues, q, list); 01711 AST_LIST_UNLOCK(&queues); 01712 destroy_queue(q); 01713 } 01714 }
static int load_module | ( | void | ) | [static] |
Definition at line 5223 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_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), ql_exec(), queue_exec(), queueagentcount_function, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().
05224 { 05225 int res; 05226 05227 if (!reload_queues()) 05228 return AST_MODULE_LOAD_DECLINE; 05229 05230 if (queue_persistent_members) 05231 reload_queue_members(); 05232 05233 ast_mutex_init(&device_state.lock); 05234 ast_cond_init(&device_state.cond, NULL); 05235 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL); 05236 05237 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 05238 res = ast_register_application(app, queue_exec, synopsis, descrip); 05239 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip); 05240 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip); 05241 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip); 05242 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip); 05243 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip); 05244 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); 05245 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); 05246 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); 05247 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); 05248 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); 05249 res |= ast_custom_function_register(&queueagentcount_function); 05250 res |= ast_custom_function_register(&queuemembercount_function); 05251 res |= ast_custom_function_register(&queuememberlist_function); 05252 res |= ast_custom_function_register(&queuewaitingcount_function); 05253 res |= ast_devstate_add(statechange_queue, NULL); 05254 05255 return res; 05256 }
static struct call_queue* load_realtime_queue | ( | const char * | queuename | ) | [static] |
Definition at line 1383 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(), member_interface::list, LOG_ERROR, call_queue::name, call_queue::realtime, and update_realtime_members().
Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_queuemembercount(), and reload_queue_members().
01384 { 01385 struct ast_variable *queue_vars; 01386 struct ast_config *member_config = NULL; 01387 struct call_queue *q; 01388 01389 /* Find the queue in the in-core list first. */ 01390 AST_LIST_LOCK(&queues); 01391 AST_LIST_TRAVERSE(&queues, q, list) { 01392 if (!strcasecmp(q->name, queuename)) { 01393 break; 01394 } 01395 } 01396 AST_LIST_UNLOCK(&queues); 01397 01398 if (!q || q->realtime) { 01399 /*! \note Load from realtime before taking the global qlock, to avoid blocking all 01400 queue operations while waiting for the DB. 01401 01402 This will be two separate database transactions, so we might 01403 see queue parameters as they were before another process 01404 changed the queue and member list as it was after the change. 01405 Thus we might see an empty member list when a queue is 01406 deleted. In practise, this is unlikely to cause a problem. */ 01407 01408 queue_vars = ast_load_realtime("queues", "name", queuename, NULL); 01409 if (queue_vars) { 01410 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL); 01411 if (!member_config) { 01412 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n"); 01413 ast_variables_destroy(queue_vars); 01414 return NULL; 01415 } 01416 } 01417 01418 AST_LIST_LOCK(&queues); 01419 01420 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 01421 if (member_config) 01422 ast_config_destroy(member_config); 01423 if (queue_vars) 01424 ast_variables_destroy(queue_vars); 01425 01426 AST_LIST_UNLOCK(&queues); 01427 } else { 01428 update_realtime_members(q); 01429 } 01430 return q; 01431 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4877 of file app_queue.c.
References add_to_queue(), ast_queue_log(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.
Referenced by load_module().
04878 { 04879 const char *queuename, *interface, *penalty_s, *paused_s, *membername; 04880 int paused, penalty = 0; 04881 04882 queuename = astman_get_header(m, "Queue"); 04883 interface = astman_get_header(m, "Interface"); 04884 penalty_s = astman_get_header(m, "Penalty"); 04885 paused_s = astman_get_header(m, "Paused"); 04886 membername = astman_get_header(m, "MemberName"); 04887 04888 if (ast_strlen_zero(queuename)) { 04889 astman_send_error(s, m, "'Queue' not specified."); 04890 return 0; 04891 } 04892 04893 if (ast_strlen_zero(interface)) { 04894 astman_send_error(s, m, "'Interface' not specified."); 04895 return 0; 04896 } 04897 04898 if (ast_strlen_zero(penalty_s)) 04899 penalty = 0; 04900 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0) 04901 penalty = 0; 04902 04903 if (ast_strlen_zero(paused_s)) 04904 paused = 0; 04905 else 04906 paused = abs(ast_true(paused_s)); 04907 04908 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) { 04909 case RES_OKAY: 04910 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 04911 astman_send_ack(s, m, "Added interface to queue"); 04912 break; 04913 case RES_EXISTS: 04914 astman_send_error(s, m, "Unable to add interface: Already there"); 04915 break; 04916 case RES_NOSUCHQUEUE: 04917 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 04918 break; 04919 case RES_OUTOFMEMORY: 04920 astman_send_error(s, m, "Out of memory"); 04921 break; 04922 } 04923 04924 return 0; 04925 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4961 of file app_queue.c.
References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_paused().
Referenced by load_module().
04962 { 04963 const char *queuename, *interface, *paused_s; 04964 int paused; 04965 04966 interface = astman_get_header(m, "Interface"); 04967 paused_s = astman_get_header(m, "Paused"); 04968 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 04969 04970 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 04971 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 04972 return 0; 04973 } 04974 04975 paused = abs(ast_true(paused_s)); 04976 04977 if (set_member_paused(queuename, interface, paused)) 04978 astman_send_error(s, m, "Interface not found"); 04979 else 04980 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 04981 return 0; 04982 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4772 of file app_queue.c.
References __queues_show(), astman_append(), RESULT_SUCCESS, and s.
Referenced by load_module().
04773 { 04774 char *a[] = { "queue", "show" }; 04775 04776 __queues_show(s, 1, -1, 2, a); 04777 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 04778 04779 return RESULT_SUCCESS; 04780 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4783 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_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::lock, call_queue::maxlen, member::membername, call_queue::members, ast_channel::name, call_queue::name, queue_ent::next, member::paused, member::penalty, RESULT_SUCCESS, call_queue::ringlimit, s, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.
Referenced by load_module().
04784 { 04785 time_t now; 04786 int pos; 04787 const char *id = astman_get_header(m,"ActionID"); 04788 const char *queuefilter = astman_get_header(m,"Queue"); 04789 const char *memberfilter = astman_get_header(m,"Member"); 04790 char idText[256] = ""; 04791 struct call_queue *q; 04792 struct queue_ent *qe; 04793 float sl = 0; 04794 struct member *mem; 04795 struct ao2_iterator mem_iter; 04796 04797 astman_send_ack(s, m, "Queue status will follow"); 04798 time(&now); 04799 AST_LIST_LOCK(&queues); 04800 if (!ast_strlen_zero(id)) 04801 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 04802 04803 AST_LIST_TRAVERSE(&queues, q, list) { 04804 ast_mutex_lock(&q->lock); 04805 04806 /* List queue properties */ 04807 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 04808 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 04809 astman_append(s, "Event: QueueParams\r\n" 04810 "Queue: %s\r\n" 04811 "Max: %d\r\n" 04812 "Calls: %d\r\n" 04813 "Holdtime: %d\r\n" 04814 "Completed: %d\r\n" 04815 "Abandoned: %d\r\n" 04816 "ServiceLevel: %d\r\n" 04817 "ServicelevelPerf: %2.1f\r\n" 04818 "RingLimit: %d\r\n" 04819 "Weight: %d\r\n" 04820 "%s" 04821 "\r\n", 04822 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, 04823 q->callsabandoned, q->servicelevel, sl, q->ringlimit, q->weight, idText); 04824 /* List Queue Members */ 04825 mem_iter = ao2_iterator_init(q->members, 0); 04826 while ((mem = ao2_iterator_next(&mem_iter))) { 04827 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) { 04828 astman_append(s, "Event: QueueMember\r\n" 04829 "Queue: %s\r\n" 04830 "Name: %s\r\n" 04831 "Location: %s\r\n" 04832 "Membership: %s\r\n" 04833 "Penalty: %d\r\n" 04834 "CallsTaken: %d\r\n" 04835 "LastCall: %d\r\n" 04836 "Status: %d\r\n" 04837 "Paused: %d\r\n" 04838 "%s" 04839 "\r\n", 04840 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 04841 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 04842 } 04843 ao2_ref(mem, -1); 04844 } 04845 /* List Queue Entries */ 04846 pos = 1; 04847 for (qe = q->head; qe; qe = qe->next) { 04848 astman_append(s, "Event: QueueEntry\r\n" 04849 "Queue: %s\r\n" 04850 "Position: %d\r\n" 04851 "Channel: %s\r\n" 04852 "CallerID: %s\r\n" 04853 "CallerIDName: %s\r\n" 04854 "Wait: %ld\r\n" 04855 "%s" 04856 "\r\n", 04857 q->name, pos++, qe->chan->name, 04858 S_OR(qe->chan->cid.cid_num, "unknown"), 04859 S_OR(qe->chan->cid.cid_name, "unknown"), 04860 (long) (now - qe->start), idText); 04861 } 04862 } 04863 ast_mutex_unlock(&q->lock); 04864 } 04865 04866 astman_append(s, 04867 "Event: QueueStatusComplete\r\n" 04868 "%s" 04869 "\r\n",idText); 04870 04871 AST_LIST_UNLOCK(&queues); 04872 04873 04874 return RESULT_SUCCESS; 04875 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4927 of file app_queue.c.
References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, and s.
Referenced by load_module().
04928 { 04929 const char *queuename, *interface; 04930 04931 queuename = astman_get_header(m, "Queue"); 04932 interface = astman_get_header(m, "Interface"); 04933 04934 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 04935 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 04936 return 0; 04937 } 04938 04939 switch (remove_from_queue(queuename, interface)) { 04940 case RES_OKAY: 04941 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 04942 astman_send_ack(s, m, "Removed interface from queue"); 04943 break; 04944 case RES_EXISTS: 04945 astman_send_error(s, m, "Unable to remove interface: Not there"); 04946 break; 04947 case RES_NOSUCHQUEUE: 04948 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 04949 break; 04950 case RES_OUTOFMEMORY: 04951 astman_send_error(s, m, "Out of memory"); 04952 break; 04953 case RES_NOT_DYNAMIC: 04954 astman_send_error(s, m, "Member not dynamic"); 04955 break; 04956 } 04957 04958 return 0; 04959 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 815 of file app_queue.c.
References member::interface.
Referenced by init_queue().
00816 { 00817 struct member *mem1 = obj1, *mem2 = obj2; 00818 return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP; 00819 }
static int member_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 803 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
00804 { 00805 const struct member *mem = obj; 00806 const char *chname = strchr(mem->interface, '/'); 00807 int ret = 0, i; 00808 if (!chname) 00809 chname = mem->interface; 00810 for (i = 0; i < 5 && chname[i]; i++) 00811 ret += compress_char(chname[i]) << (i * 6); 00812 return ret; 00813 }
static void monjoin_dep_warning | ( | void | ) | [static] |
Definition at line 474 of file app_queue.c.
References ast_log(), and LOG_NOTICE.
Referenced by queue_set_param().
00475 { 00476 static unsigned int warned = 0; 00477 if (!warned) { 00478 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n"); 00479 warned = 1; 00480 } 00481 }
static int play_file | ( | struct ast_channel * | chan, | |
char * | filename | |||
) | [static] |
Definition at line 1498 of file app_queue.c.
References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), queue_ent::chan, and ast_channel::language.
Referenced by say_periodic_announcement(), say_position(), and try_calling().
01499 { 01500 int res; 01501 01502 if (ast_strlen_zero(filename)) { 01503 return 0; 01504 } 01505 01506 ast_stopstream(chan); 01507 01508 res = ast_streamfile(chan, filename, chan->language); 01509 if (!res) 01510 res = ast_waitstream(chan, AST_DIGIT_ANY); 01511 01512 ast_stopstream(chan); 01513 01514 return res; 01515 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3601 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_module_user::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().
Referenced by load_module().
03602 { 03603 struct ast_module_user *lu; 03604 char *parse; 03605 int priority_jump = 0; 03606 int ignore_fail = 0; 03607 AST_DECLARE_APP_ARGS(args, 03608 AST_APP_ARG(queuename); 03609 AST_APP_ARG(interface); 03610 AST_APP_ARG(options); 03611 ); 03612 03613 if (ast_strlen_zero(data)) { 03614 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03615 return -1; 03616 } 03617 03618 parse = ast_strdupa(data); 03619 03620 AST_STANDARD_APP_ARGS(args, parse); 03621 03622 lu = ast_module_user_add(chan); 03623 03624 if (args.options) { 03625 if (strchr(args.options, 'j')) 03626 priority_jump = 1; 03627 if (strchr(args.options, 'i')) 03628 ignore_fail = 1; 03629 } 03630 03631 if (ast_strlen_zero(args.interface)) { 03632 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03633 ast_module_user_remove(lu); 03634 return -1; 03635 } 03636 03637 if (set_member_paused(args.queuename, args.interface, 1)) { 03638 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 03639 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 03640 if (priority_jump || ast_opt_priority_jumping) { 03641 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03642 ast_module_user_remove(lu); 03643 return 0; 03644 } 03645 } 03646 ast_module_user_remove(lu); 03647 if (ignore_fail) { 03648 return 0; 03649 } else { 03650 return -1; 03651 } 03652 } 03653 03654 ast_module_user_remove(lu); 03655 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 03656 return 0; 03657 }
static int ql_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3858 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(), ast_module_user::chan, LOG_WARNING, and parse().
Referenced by load_module().
03859 { 03860 struct ast_module_user *u; 03861 char *parse; 03862 03863 AST_DECLARE_APP_ARGS(args, 03864 AST_APP_ARG(queuename); 03865 AST_APP_ARG(uniqueid); 03866 AST_APP_ARG(membername); 03867 AST_APP_ARG(event); 03868 AST_APP_ARG(params); 03869 ); 03870 03871 if (ast_strlen_zero(data)) { 03872 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n"); 03873 return -1; 03874 } 03875 03876 u = ast_module_user_add(chan); 03877 03878 parse = ast_strdupa(data); 03879 03880 AST_STANDARD_APP_ARGS(args, parse); 03881 03882 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 03883 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 03884 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n"); 03885 ast_module_user_remove(u); 03886 return -1; 03887 } 03888 03889 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 03890 "%s", args.params ? args.params : ""); 03891 03892 ast_module_user_remove(u); 03893 03894 return 0; 03895 }
static int queue_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
The starting point for all queue calls.
The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again uless some condition (such as an expiration time) causes us to exit the queue.
Definition at line 3909 of file app_queue.c.
References AST_APP_ARG, AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_module_user_add, 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, get_member_status(), is_our_turn(), join_queue(), leave_queue(), LOG_DEBUG, LOG_WARNING, ast_channel::name, option_debug, option_verbose, parse(), pbx_builtin_getvar_helper(), QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), stop, try_calling(), ast_channel::uniqueid, update_realtime_members(), VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().
Referenced by load_module().
03910 { 03911 int res=-1; 03912 int ringing=0; 03913 struct ast_module_user *lu; 03914 const char *user_priority; 03915 const char *max_penalty_str; 03916 int prio; 03917 int max_penalty; 03918 enum queue_result reason = QUEUE_UNKNOWN; 03919 /* whether to exit Queue application after the timeout hits */ 03920 int tries = 0; 03921 int noption = 0; 03922 char *parse; 03923 AST_DECLARE_APP_ARGS(args, 03924 AST_APP_ARG(queuename); 03925 AST_APP_ARG(options); 03926 AST_APP_ARG(url); 03927 AST_APP_ARG(announceoverride); 03928 AST_APP_ARG(queuetimeoutstr); 03929 AST_APP_ARG(agi); 03930 ); 03931 /* Our queue entry */ 03932 struct queue_ent qe; 03933 03934 if (ast_strlen_zero(data)) { 03935 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n"); 03936 return -1; 03937 } 03938 03939 parse = ast_strdupa(data); 03940 AST_STANDARD_APP_ARGS(args, parse); 03941 03942 lu = ast_module_user_add(chan); 03943 03944 /* Setup our queue entry */ 03945 memset(&qe, 0, sizeof(qe)); 03946 qe.start = time(NULL); 03947 03948 /* set the expire time based on the supplied timeout; */ 03949 if (!ast_strlen_zero(args.queuetimeoutstr)) 03950 qe.expire = qe.start + atoi(args.queuetimeoutstr); 03951 else 03952 qe.expire = 0; 03953 03954 /* Get the priority from the variable ${QUEUE_PRIO} */ 03955 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 03956 if (user_priority) { 03957 if (sscanf(user_priority, "%d", &prio) == 1) { 03958 if (option_debug) 03959 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", 03960 chan->name, prio); 03961 } else { 03962 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 03963 user_priority, chan->name); 03964 prio = 0; 03965 } 03966 } else { 03967 if (option_debug > 2) 03968 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); 03969 prio = 0; 03970 } 03971 03972 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 03973 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 03974 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) { 03975 if (option_debug) 03976 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", 03977 chan->name, max_penalty); 03978 } else { 03979 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 03980 max_penalty_str, chan->name); 03981 max_penalty = 0; 03982 } 03983 } else { 03984 max_penalty = 0; 03985 } 03986 03987 if (args.options && (strchr(args.options, 'r'))) 03988 ringing = 1; 03989 03990 if (option_debug) 03991 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 03992 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 03993 03994 qe.chan = chan; 03995 qe.prio = prio; 03996 qe.max_penalty = max_penalty; 03997 qe.last_pos_said = 0; 03998 qe.last_pos = 0; 03999 qe.last_periodic_announce_time = time(NULL); 04000 qe.last_periodic_announce_sound = 0; 04001 qe.valid_digits = 0; 04002 if (!join_queue(args.queuename, &qe, &reason)) { 04003 int makeannouncement = 0; 04004 04005 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), 04006 S_OR(chan->cid.cid_num, "")); 04007 check_turns: 04008 if (ringing) { 04009 ast_indicate(chan, AST_CONTROL_RINGING); 04010 } else { 04011 ast_moh_start(chan, qe.moh, NULL); 04012 } 04013 04014 /* This is the wait loop for callers 2 through maxlen */ 04015 res = wait_our_turn(&qe, ringing, &reason); 04016 if (res) 04017 goto stop; 04018 04019 for (;;) { 04020 /* This is the wait loop for the head caller*/ 04021 /* To exit, they may get their call answered; */ 04022 /* they may dial a digit from the queue context; */ 04023 /* or, they may timeout. */ 04024 04025 enum queue_member_status stat; 04026 04027 /* Leave if we have exceeded our queuetimeout */ 04028 if (qe.expire && (time(NULL) >= qe.expire)) { 04029 record_abandoned(&qe); 04030 reason = QUEUE_TIMEOUT; 04031 res = 0; 04032 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04033 break; 04034 } 04035 04036 if (makeannouncement) { 04037 /* Make a position announcement, if enabled */ 04038 if (qe.parent->announcefrequency && !ringing) 04039 if ((res = say_position(&qe))) 04040 goto stop; 04041 04042 } 04043 makeannouncement = 1; 04044 04045 /* Leave if we have exceeded our queuetimeout */ 04046 if (qe.expire && (time(NULL) >= qe.expire)) { 04047 record_abandoned(&qe); 04048 reason = QUEUE_TIMEOUT; 04049 res = 0; 04050 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04051 break; 04052 } 04053 /* Make a periodic announcement, if enabled */ 04054 if (qe.parent->periodicannouncefrequency && !ringing) 04055 if ((res = say_periodic_announcement(&qe))) 04056 goto stop; 04057 04058 /* Leave if we have exceeded our queuetimeout */ 04059 if (qe.expire && (time(NULL) >= qe.expire)) { 04060 record_abandoned(&qe); 04061 reason = QUEUE_TIMEOUT; 04062 res = 0; 04063 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04064 break; 04065 } 04066 /* Try calling all queue members for 'timeout' seconds */ 04067 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi); 04068 if (res) 04069 goto stop; 04070 04071 stat = get_member_status(qe.parent, qe.max_penalty); 04072 04073 /* exit after 'timeout' cycle if 'n' option enabled */ 04074 if (noption && tries >= qe.parent->membercount) { 04075 if (option_verbose > 2) 04076 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n"); 04077 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04078 record_abandoned(&qe); 04079 reason = QUEUE_TIMEOUT; 04080 res = 0; 04081 break; 04082 } 04083 04084 /* leave the queue if no agents, if enabled */ 04085 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 04086 record_abandoned(&qe); 04087 reason = QUEUE_LEAVEEMPTY; 04088 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04089 res = 0; 04090 break; 04091 } 04092 04093 /* leave the queue if no reachable agents, if enabled */ 04094 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 04095 record_abandoned(&qe); 04096 reason = QUEUE_LEAVEUNAVAIL; 04097 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04098 res = 0; 04099 break; 04100 } 04101 04102 /* Leave if we have exceeded our queuetimeout */ 04103 if (qe.expire && (time(NULL) >= qe.expire)) { 04104 record_abandoned(&qe); 04105 reason = QUEUE_TIMEOUT; 04106 res = 0; 04107 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04108 break; 04109 } 04110 04111 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 04112 update_realtime_members(qe.parent); 04113 04114 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 04115 res = wait_a_bit(&qe); 04116 if (res) 04117 goto stop; 04118 04119 /* Since this is a priority queue and 04120 * it is not sure that we are still at the head 04121 * of the queue, go and check for our turn again. 04122 */ 04123 if (!is_our_turn(&qe)) { 04124 if (option_debug) 04125 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n", 04126 qe.chan->name); 04127 goto check_turns; 04128 } 04129 } 04130 04131 stop: 04132 if (res) { 04133 if (res < 0) { 04134 if (!qe.handled) { 04135 record_abandoned(&qe); 04136 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 04137 "%d|%d|%ld", qe.pos, qe.opos, 04138 (long) time(NULL) - qe.start); 04139 } 04140 res = -1; 04141 } else if (qe.valid_digits) { 04142 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 04143 "%s|%d", qe.digits, qe.pos); 04144 } 04145 } 04146 04147 /* Don't allow return code > 0 */ 04148 if (res >= 0) { 04149 res = 0; 04150 if (ringing) { 04151 ast_indicate(chan, -1); 04152 } else { 04153 ast_moh_stop(chan); 04154 } 04155 ast_stopstream(chan); 04156 } 04157 leave_queue(&qe); 04158 if (reason != QUEUE_UNKNOWN) 04159 set_queue_result(chan, reason); 04160 } else { 04161 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 04162 set_queue_result(chan, reason); 04163 res = 0; 04164 } 04165 ast_module_user_remove(lu); 04166 04167 return res; 04168 }
static int queue_function_queuemembercount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4178 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, ast_strlen_zero(), load_realtime_queue(), call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::members, name, member::paused, QMC_ACTIVE, QMC_FREE, QMC_PAUSED, QMC_VALID, and member::status.
04179 { 04180 int count = 0; 04181 struct call_queue *q; 04182 struct ast_module_user *lu; 04183 struct member *m; 04184 struct ao2_iterator mem_iter; 04185 char *name, *item; 04186 enum qmc_status mode = QMC_VALID; 04187 04188 buf[0] = '\0'; 04189 04190 if (ast_strlen_zero(data)) { 04191 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04192 return -1; 04193 } 04194 04195 name = ast_strdupa(data); 04196 04197 lu = ast_module_user_add(chan); 04198 04199 if ((item = strchr(name, ':'))) { 04200 *item = '\0'; 04201 item++; 04202 } else { 04203 item = ""; 04204 } 04205 04206 if (!strcasecmp(item, "valid")) { 04207 mode = QMC_VALID; 04208 } else if (!strcasecmp(item, "paused")) { 04209 mode = QMC_PAUSED; 04210 } else if (!strcasecmp(item, "active")) { 04211 mode = QMC_ACTIVE; 04212 } else if (!strcasecmp(item, "free")) { 04213 mode = QMC_FREE; 04214 } else if (!strcasecmp(item, "all")) { 04215 mode = QMC_ALL; 04216 } 04217 04218 if ((q = load_realtime_queue(name))) { 04219 ast_mutex_lock(&q->lock); 04220 mem_iter = ao2_iterator_init(q->members, 0); 04221 while ((m = ao2_iterator_next(&mem_iter))) { 04222 switch (mode) { 04223 case QMC_VALID: 04224 /* Count the queue members who are logged in and presently answering calls */ 04225 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 04226 count++; 04227 } 04228 break; 04229 case QMC_PAUSED: 04230 /* Count paused members */ 04231 if (m->paused) { 04232 count++; 04233 } 04234 break; 04235 case QMC_ACTIVE: 04236 /* Count not paused members who are logged in and presently answering calls */ 04237 if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 04238 count++; 04239 } 04240 break; 04241 case QMC_FREE: 04242 /* Count free members in the queue */ 04243 if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) { 04244 count++; 04245 } 04246 break; 04247 default: 04248 count++; 04249 break; 04250 } 04251 ao2_ref(m, -1); 04252 } 04253 ast_mutex_unlock(&q->lock); 04254 } else 04255 ast_log(LOG_WARNING, "queue %s was not found\n", name); 04256 04257 snprintf(buf, len, "%d", count); 04258 ast_module_user_remove(lu); 04259 04260 return 0; 04261 }
static int queue_function_queuememberlist | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4306 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), call_queue::lock, LOG_ERROR, LOG_WARNING, member::membername, call_queue::members, and call_queue::name.
04307 { 04308 struct ast_module_user *u; 04309 struct call_queue *q; 04310 struct member *m; 04311 04312 /* Ensure an otherwise empty list doesn't return garbage */ 04313 buf[0] = '\0'; 04314 04315 if (ast_strlen_zero(data)) { 04316 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 04317 return -1; 04318 } 04319 04320 u = ast_module_user_add(chan); 04321 04322 AST_LIST_LOCK(&queues); 04323 AST_LIST_TRAVERSE(&queues, q, list) { 04324 if (!strcasecmp(q->name, data)) { 04325 ast_mutex_lock(&q->lock); 04326 break; 04327 } 04328 } 04329 AST_LIST_UNLOCK(&queues); 04330 04331 if (q) { 04332 int buflen = 0, count = 0; 04333 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 04334 04335 while ((m = ao2_iterator_next(&mem_iter))) { 04336 /* strcat() is always faster than printf() */ 04337 if (count++) { 04338 strncat(buf + buflen, ",", len - buflen - 1); 04339 buflen++; 04340 } 04341 strncat(buf + buflen, m->membername, len - buflen - 1); 04342 buflen += strlen(m->membername); 04343 /* Safeguard against overflow (negative length) */ 04344 if (buflen >= len - 2) { 04345 ao2_ref(m, -1); 04346 ast_log(LOG_WARNING, "Truncating list\n"); 04347 break; 04348 } 04349 ao2_ref(m, -1); 04350 } 04351 ast_mutex_unlock(&q->lock); 04352 } else 04353 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04354 04355 /* We should already be terminated, but let's make sure. */ 04356 buf[len - 1] = '\0'; 04357 ast_module_user_remove(u); 04358 04359 return 0; 04360 }
static int queue_function_queuewaitingcount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4263 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), ast_variables_destroy(), call_queue::count, call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::name, and var.
04264 { 04265 int count = 0; 04266 struct call_queue *q; 04267 struct ast_module_user *lu; 04268 struct ast_variable *var = NULL; 04269 04270 buf[0] = '\0'; 04271 04272 if (ast_strlen_zero(data)) { 04273 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04274 return -1; 04275 } 04276 04277 lu = ast_module_user_add(chan); 04278 04279 AST_LIST_LOCK(&queues); 04280 AST_LIST_TRAVERSE(&queues, q, list) { 04281 if (!strcasecmp(q->name, data)) { 04282 ast_mutex_lock(&q->lock); 04283 break; 04284 } 04285 } 04286 AST_LIST_UNLOCK(&queues); 04287 04288 if (q) { 04289 count = q->count; 04290 ast_mutex_unlock(&q->lock); 04291 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) { 04292 /* if the queue is realtime but was not found in memory, this 04293 * means that the queue had been deleted from memory since it was 04294 * "dead." This means it has a 0 waiting count 04295 */ 04296 count = 0; 04297 ast_variables_destroy(var); 04298 } else 04299 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04300 04301 snprintf(buf, len, "%d", count); 04302 ast_module_user_remove(lu); 04303 return 0; 04304 }
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 972 of file app_queue.c.
References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_copy_string(), ast_log(), ast_strdupa, ast_true(), call_queue::autofill, call_queue::autopause, call_queue::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::moh, call_queue::monfmt, call_queue::monjoin, monjoin_dep_warning(), call_queue::montype, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_RINGALL, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, strat2int(), call_queue::strategy, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.
Referenced by find_queue_by_name_rt(), and reload_queues().
00973 { 00974 if (!strcasecmp(param, "musicclass") || 00975 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 00976 ast_copy_string(q->moh, val, sizeof(q->moh)); 00977 } else if (!strcasecmp(param, "announce")) { 00978 ast_copy_string(q->announce, val, sizeof(q->announce)); 00979 } else if (!strcasecmp(param, "context")) { 00980 ast_copy_string(q->context, val, sizeof(q->context)); 00981 } else if (!strcasecmp(param, "timeout")) { 00982 q->timeout = atoi(val); 00983 if (q->timeout < 0) 00984 q->timeout = DEFAULT_TIMEOUT; 00985 } else if (!strcasecmp(param, "ringinuse")) { 00986 q->ringinuse = ast_true(val); 00987 } else if (!strcasecmp(param, "setinterfacevar")) { 00988 q->setinterfacevar = ast_true(val); 00989 } else if (!strcasecmp(param, "monitor-join")) { 00990 monjoin_dep_warning(); 00991 q->monjoin = ast_true(val); 00992 } else if (!strcasecmp(param, "monitor-format")) { 00993 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 00994 } else if (!strcasecmp(param, "queue-youarenext")) { 00995 ast_copy_string(q->sound_next, val, sizeof(q->sound_next)); 00996 } else if (!strcasecmp(param, "queue-thereare")) { 00997 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare)); 00998 } else if (!strcasecmp(param, "queue-callswaiting")) { 00999 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls)); 01000 } else if (!strcasecmp(param, "queue-holdtime")) { 01001 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime)); 01002 } else if (!strcasecmp(param, "queue-minutes")) { 01003 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes)); 01004 } else if (!strcasecmp(param, "queue-seconds")) { 01005 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds)); 01006 } else if (!strcasecmp(param, "queue-lessthan")) { 01007 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan)); 01008 } else if (!strcasecmp(param, "queue-thankyou")) { 01009 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks)); 01010 } else if (!strcasecmp(param, "queue-reporthold")) { 01011 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold)); 01012 } else if (!strcasecmp(param, "announce-frequency")) { 01013 q->announcefrequency = atoi(val); 01014 } else if (!strcasecmp(param, "announce-round-seconds")) { 01015 q->roundingseconds = atoi(val); 01016 if (q->roundingseconds>60 || q->roundingseconds<0) { 01017 if (linenum >= 0) { 01018 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01019 "using 0 instead for queue '%s' at line %d of queues.conf\n", 01020 val, param, q->name, linenum); 01021 } else { 01022 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01023 "using 0 instead for queue '%s'\n", val, param, q->name); 01024 } 01025 q->roundingseconds=0; 01026 } 01027 } else if (!strcasecmp(param, "announce-holdtime")) { 01028 if (!strcasecmp(val, "once")) 01029 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 01030 else if (ast_true(val)) 01031 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 01032 else 01033 q->announceholdtime = 0; 01034 } else if (!strcasecmp(param, "periodic-announce")) { 01035 if (strchr(val, '|')) { 01036 char *s, *buf = ast_strdupa(val); 01037 unsigned int i = 0; 01038 01039 while ((s = strsep(&buf, "|"))) { 01040 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i])); 01041 i++; 01042 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 01043 break; 01044 } 01045 } else { 01046 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0])); 01047 } 01048 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 01049 q->periodicannouncefrequency = atoi(val); 01050 } else if (!strcasecmp(param, "retry")) { 01051 q->retry = atoi(val); 01052 if (q->retry <= 0) 01053 q->retry = DEFAULT_RETRY; 01054 } else if (!strcasecmp(param, "wrapuptime")) { 01055 q->wrapuptime = atoi(val); 01056 } else if (!strcasecmp(param, "autofill")) { 01057 q->autofill = ast_true(val); 01058 } else if (!strcasecmp(param, "monitor-type")) { 01059 if (!strcasecmp(val, "mixmonitor")) 01060 q->montype = 1; 01061 } else if (!strcasecmp(param, "autopause")) { 01062 q->autopause = ast_true(val); 01063 } else if (!strcasecmp(param, "maxlen")) { 01064 q->maxlen = atoi(val); 01065 if (q->maxlen < 0) 01066 q->maxlen = 0; 01067 } else if (!strcasecmp(param, "ringlimit")) { 01068 q->ringlimit = atoi(val); 01069 if (q->ringlimit < 0) 01070 q->ringlimit = 0; 01071 } else if (!strcasecmp(param, "servicelevel")) { 01072 q->servicelevel= atoi(val); 01073 } else if (!strcasecmp(param, "strategy")) { 01074 q->strategy = strat2int(val); 01075 if (q->strategy < 0) { 01076 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01077 val, q->name); 01078 q->strategy = QUEUE_STRATEGY_RINGALL; 01079 } 01080 } else if (!strcasecmp(param, "joinempty")) { 01081 if (!strcasecmp(val, "strict")) 01082 q->joinempty = QUEUE_EMPTY_STRICT; 01083 else if (ast_true(val)) 01084 q->joinempty = QUEUE_EMPTY_NORMAL; 01085 else 01086 q->joinempty = 0; 01087 } else if (!strcasecmp(param, "leavewhenempty")) { 01088 if (!strcasecmp(val, "strict")) 01089 q->leavewhenempty = QUEUE_EMPTY_STRICT; 01090 else if (ast_true(val)) 01091 q->leavewhenempty = QUEUE_EMPTY_NORMAL; 01092 else 01093 q->leavewhenempty = 0; 01094 } else if (!strcasecmp(param, "eventmemberstatus")) { 01095 q->maskmemberstatus = !ast_true(val); 01096 } else if (!strcasecmp(param, "eventwhencalled")) { 01097 if (!strcasecmp(val, "vars")) { 01098 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 01099 } else { 01100 q->eventwhencalled = ast_true(val) ? 1 : 0; 01101 } 01102 } else if (!strcasecmp(param, "reportholdtime")) { 01103 q->reportholdtime = ast_true(val); 01104 } else if (!strcasecmp(param, "memberdelay")) { 01105 q->memberdelay = atoi(val); 01106 } else if (!strcasecmp(param, "weight")) { 01107 q->weight = atoi(val); 01108 if (q->weight) 01109 use_weight++; 01110 /* With Realtime queues, if the last queue using weights is deleted in realtime, 01111 we will not see any effect on use_weight until next reload. */ 01112 } else if (!strcasecmp(param, "timeoutrestart")) { 01113 q->timeoutrestart = ast_true(val); 01114 } else if (failunknown) { 01115 if (linenum >= 0) { 01116 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 01117 q->name, param, linenum); 01118 } else { 01119 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 01120 } 01121 } 01122 }
static int queue_show | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 4738 of file app_queue.c.
References __queues_show().
Referenced by __queues_show().
04739 { 04740 return __queues_show(NULL, 0, fd, argc, argv); 04741 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 2615 of file app_queue.c.
References ast_free.
02616 { 02617 struct queue_transfer_ds *qtds = data; 02618 ast_free(qtds); 02619 }
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 2638 of file app_queue.c.
References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, ast_channel::uniqueid, and update_queue().
02639 { 02640 struct queue_transfer_ds *qtds = data; 02641 struct queue_ent *qe = qtds->qe; 02642 struct member *member = qtds->member; 02643 time_t callstart = qtds->starttime; 02644 int callcompletedinsl = qtds->callcompletedinsl; 02645 struct ast_datastore *datastore; 02646 02647 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 02648 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 02649 (long) (time(NULL) - callstart)); 02650 02651 update_queue(qe->parent, member, callcompletedinsl); 02652 02653 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 02654 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 02655 ast_channel_datastore_remove(old_chan, datastore); 02656 } else { 02657 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 02658 } 02659 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 1658 of file app_queue.c.
References ast_mutex_lock(), ast_mutex_unlock(), call_queue::holdtime, call_queue::lock, and queue_ent::parent.
Referenced by try_calling().
01659 { 01660 int oldvalue; 01661 01662 /* Calculate holdtime using an exponential average */ 01663 /* Thanks to SRT for this contribution */ 01664 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 01665 01666 ast_mutex_lock(&qe->parent->lock); 01667 oldvalue = qe->parent->holdtime; 01668 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 01669 ast_mutex_unlock(&qe->parent->lock); 01670 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 2078 of file app_queue.c.
References ast_mutex_lock(), ast_mutex_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::lock, manager_event(), call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, queue_ent::start, and ast_channel::uniqueid.
Referenced by queue_exec(), and try_calling().
02079 { 02080 ast_mutex_lock(&qe->parent->lock); 02081 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 02082 "Queue: %s\r\n" 02083 "Uniqueid: %s\r\n" 02084 "Position: %d\r\n" 02085 "OriginalPosition: %d\r\n" 02086 "HoldTime: %d\r\n", 02087 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 02088 02089 qe->parent->callsabandoned++; 02090 ast_mutex_unlock(&qe->parent->lock); 02091 }
static int reload | ( | void | ) | [static] |
Definition at line 5258 of file app_queue.c.
References reload_queues().
05259 { 05260 reload_queues(); 05261 return 0; 05262 }
static void reload_queue_members | ( | void | ) | [static] |
Definition at line 3506 of file app_queue.c.
References add_to_queue(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), errno, member::interface, ast_db_entry::key, call_queue::list, load_realtime_queue(), call_queue::lock, LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, call_queue::name, ast_db_entry::next, option_debug, member::paused, member::penalty, PM_MAX_LEN, and RES_OUTOFMEMORY.
Referenced by load_module().
03507 { 03508 char *cur_ptr; 03509 char *queue_name; 03510 char *member; 03511 char *interface; 03512 char *membername = NULL; 03513 char *penalty_tok; 03514 int penalty = 0; 03515 char *paused_tok; 03516 int paused = 0; 03517 struct ast_db_entry *db_tree; 03518 struct ast_db_entry *entry; 03519 struct call_queue *cur_queue; 03520 char queue_data[PM_MAX_LEN]; 03521 03522 AST_LIST_LOCK(&queues); 03523 03524 /* Each key in 'pm_family' is the name of a queue */ 03525 db_tree = ast_db_gettree(pm_family, NULL); 03526 for (entry = db_tree; entry; entry = entry->next) { 03527 03528 queue_name = entry->key + strlen(pm_family) + 2; 03529 03530 AST_LIST_TRAVERSE(&queues, cur_queue, list) { 03531 ast_mutex_lock(&cur_queue->lock); 03532 if (!strcmp(queue_name, cur_queue->name)) 03533 break; 03534 ast_mutex_unlock(&cur_queue->lock); 03535 } 03536 03537 if (!cur_queue) 03538 cur_queue = load_realtime_queue(queue_name); 03539 03540 if (!cur_queue) { 03541 /* If the queue no longer exists, remove it from the 03542 * database */ 03543 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 03544 ast_db_del(pm_family, queue_name); 03545 continue; 03546 } else 03547 ast_mutex_unlock(&cur_queue->lock); 03548 03549 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) 03550 continue; 03551 03552 cur_ptr = queue_data; 03553 while ((member = strsep(&cur_ptr, "|"))) { 03554 if (ast_strlen_zero(member)) 03555 continue; 03556 03557 interface = strsep(&member, ";"); 03558 penalty_tok = strsep(&member, ";"); 03559 paused_tok = strsep(&member, ";"); 03560 membername = strsep(&member, ";"); 03561 03562 if (!penalty_tok) { 03563 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 03564 break; 03565 } 03566 penalty = strtol(penalty_tok, NULL, 10); 03567 if (errno == ERANGE) { 03568 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 03569 break; 03570 } 03571 03572 if (!paused_tok) { 03573 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 03574 break; 03575 } 03576 paused = strtol(paused_tok, NULL, 10); 03577 if ((errno == ERANGE) || paused < 0 || paused > 1) { 03578 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 03579 break; 03580 } 03581 if (ast_strlen_zero(membername)) 03582 membername = interface; 03583 03584 if (option_debug) 03585 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 03586 03587 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) { 03588 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 03589 break; 03590 } 03591 } 03592 } 03593 03594 AST_LIST_UNLOCK(&queues); 03595 if (db_tree) { 03596 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 03597 ast_db_freetree(db_tree); 03598 } 03599 }
static int reload_queues | ( | void | ) | [static] |
Definition at line 4405 of file app_queue.c.
References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), AST_APP_ARG, ast_category_browse(), ast_config_load(), ast_copy_string(), AST_DECLARE_APP_ARGS, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), AST_NONSTANDARD_APP_ARGS, 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, call_queue::lock, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), call_queue::strategy, and var.
Referenced by load_module(), and reload().
04406 { 04407 struct call_queue *q; 04408 struct ast_config *cfg; 04409 char *cat, *tmp; 04410 struct ast_variable *var; 04411 struct member *cur, *newm; 04412 struct ao2_iterator mem_iter; 04413 int new; 04414 const char *general_val = NULL; 04415 char parse[80]; 04416 char *interface; 04417 char *membername = NULL; 04418 int penalty; 04419 AST_DECLARE_APP_ARGS(args, 04420 AST_APP_ARG(interface); 04421 AST_APP_ARG(penalty); 04422 AST_APP_ARG(membername); 04423 ); 04424 04425 if (!(cfg = ast_config_load("queues.conf"))) { 04426 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 04427 return 0; 04428 } 04429 AST_LIST_LOCK(&queues); 04430 use_weight=0; 04431 /* Mark all non-realtime queues as dead for the moment */ 04432 AST_LIST_TRAVERSE(&queues, q, list) { 04433 if (!q->realtime) { 04434 q->dead = 1; 04435 q->found = 0; 04436 } 04437 } 04438 04439 /* Chug through config file */ 04440 cat = NULL; 04441 while ((cat = ast_category_browse(cfg, cat)) ) { 04442 if (!strcasecmp(cat, "general")) { 04443 /* Initialize global settings */ 04444 queue_debug = 0; 04445 if ((general_val = ast_variable_retrieve(cfg, "general", "debug"))) 04446 queue_debug = ast_true(general_val); 04447 queue_persistent_members = 0; 04448 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 04449 queue_persistent_members = ast_true(general_val); 04450 autofill_default = 0; 04451 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 04452 autofill_default = ast_true(general_val); 04453 montype_default = 0; 04454 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) 04455 if (!strcasecmp(general_val, "mixmonitor")) 04456 montype_default = 1; 04457 } else { /* Define queue */ 04458 /* Look for an existing one */ 04459 AST_LIST_TRAVERSE(&queues, q, list) { 04460 if (!strcmp(q->name, cat)) 04461 break; 04462 } 04463 if (!q) { 04464 /* Make one then */ 04465 if (!(q = alloc_queue(cat))) { 04466 /* TODO: Handle memory allocation failure */ 04467 } 04468 new = 1; 04469 } else 04470 new = 0; 04471 if (q) { 04472 if (!new) 04473 ast_mutex_lock(&q->lock); 04474 /* Check if a queue with this name already exists */ 04475 if (q->found) { 04476 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat); 04477 if (!new) 04478 ast_mutex_unlock(&q->lock); 04479 continue; 04480 } 04481 /* Re-initialize the queue, and clear statistics */ 04482 init_queue(q); 04483 clear_queue(q); 04484 mem_iter = ao2_iterator_init(q->members, 0); 04485 while ((cur = ao2_iterator_next(&mem_iter))) { 04486 if (!cur->dynamic) { 04487 cur->delme = 1; 04488 } 04489 ao2_ref(cur, -1); 04490 } 04491 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 04492 if (!strcasecmp(var->name, "member")) { 04493 struct member tmpmem; 04494 membername = NULL; 04495 04496 /* Add a new member */ 04497 ast_copy_string(parse, var->value, sizeof(parse)); 04498 04499 AST_NONSTANDARD_APP_ARGS(args, parse, ','); 04500 04501 interface = args.interface; 04502 if (!ast_strlen_zero(args.penalty)) { 04503 tmp = args.penalty; 04504 while (*tmp && *tmp < 33) tmp++; 04505 penalty = atoi(tmp); 04506 if (penalty < 0) { 04507 penalty = 0; 04508 } 04509 } else 04510 penalty = 0; 04511 04512 if (!ast_strlen_zero(args.membername)) { 04513 membername = args.membername; 04514 while (*membername && *membername < 33) membername++; 04515 } 04516 04517 /* Find the old position in the list */ 04518 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04519 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 04520 04521 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); 04522 ao2_link(q->members, newm); 04523 ao2_ref(newm, -1); 04524 newm = NULL; 04525 04526 if (cur) 04527 ao2_ref(cur, -1); 04528 else { 04529 /* Add them to the master int list if necessary */ 04530 add_to_interfaces(interface); 04531 q->membercount++; 04532 } 04533 } else { 04534 queue_set_param(q, var->name, var->value, var->lineno, 1); 04535 } 04536 } 04537 04538 /* Free remaining members marked as delme */ 04539 mem_iter = ao2_iterator_init(q->members, 0); 04540 while ((cur = ao2_iterator_next(&mem_iter))) { 04541 if (! cur->delme) { 04542 ao2_ref(cur, -1); 04543 continue; 04544 } 04545 04546 q->membercount--; 04547 ao2_unlink(q->members, cur); 04548 remove_from_interfaces(cur->interface); 04549 ao2_ref(cur, -1); 04550 } 04551 04552 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 04553 rr_dep_warning(); 04554 04555 if (new) { 04556 AST_LIST_INSERT_HEAD(&queues, q, list); 04557 } else 04558 ast_mutex_unlock(&q->lock); 04559 } 04560 } 04561 } 04562 ast_config_destroy(cfg); 04563 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) { 04564 if (q->dead) { 04565 AST_LIST_REMOVE_CURRENT(&queues, list); 04566 if (!q->count) 04567 destroy_queue(q); 04568 else 04569 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n"); 04570 } else { 04571 ast_mutex_lock(&q->lock); 04572 mem_iter = ao2_iterator_init(q->members, 0); 04573 while ((cur = ao2_iterator_next(&mem_iter))) { 04574 if (cur->dynamic) 04575 q->membercount++; 04576 cur->status = ast_device_state(cur->interface); 04577 ao2_ref(cur, -1); 04578 } 04579 ast_mutex_unlock(&q->lock); 04580 } 04581 } 04582 AST_LIST_TRAVERSE_SAFE_END; 04583 AST_LIST_UNLOCK(&queues); 04584 return 1; 04585 }
static int remove_from_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 932 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(), member_interface::list, LOG_DEBUG, and option_debug.
Referenced by free_members(), reload_queues(), remove_from_queue(), and update_realtime_members().
00933 { 00934 struct member_interface *curint; 00935 00936 if (interface_exists_global(interface)) 00937 return 0; 00938 00939 AST_LIST_LOCK(&interfaces); 00940 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) { 00941 if (!strcasecmp(curint->interface, interface)) { 00942 if (option_debug) 00943 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface); 00944 AST_LIST_REMOVE_CURRENT(&interfaces, list); 00945 free(curint); 00946 break; 00947 } 00948 } 00949 AST_LIST_TRAVERSE_SAFE_END; 00950 AST_LIST_UNLOCK(&interfaces); 00951 00952 return 0; 00953 }
static int remove_from_queue | ( | const char * | queuename, | |
const char * | interface | |||
) | [static] |
Definition at line 3352 of file app_queue.c.
References ao2_find(), ao2_ref(), ao2_unlink(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, call_queue::lock, manager_event(), call_queue::membercount, member::membername, call_queue::members, call_queue::name, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.
Referenced by attempt_thread(), handle_queue_remove_member(), manager_remove_queue_member(), rqm_exec(), and scan_service().
03353 { 03354 struct call_queue *q; 03355 struct member *mem, tmpmem; 03356 int res = RES_NOSUCHQUEUE; 03357 03358 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 03359 03360 AST_LIST_LOCK(&queues); 03361 AST_LIST_TRAVERSE(&queues, q, list) { 03362 ast_mutex_lock(&q->lock); 03363 if (strcmp(q->name, queuename)) { 03364 ast_mutex_unlock(&q->lock); 03365 continue; 03366 } 03367 03368 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 03369 /* XXX future changes should beware of this assumption!! */ 03370 if (!mem->dynamic) { 03371 res = RES_NOT_DYNAMIC; 03372 ao2_ref(mem, -1); 03373 ast_mutex_unlock(&q->lock); 03374 break; 03375 } 03376 q->membercount--; 03377 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 03378 "Queue: %s\r\n" 03379 "Location: %s\r\n" 03380 "MemberName: %s\r\n", 03381 q->name, mem->interface, mem->membername); 03382 ao2_unlink(q->members, mem); 03383 ao2_ref(mem, -1); 03384 03385 if (queue_persistent_members) 03386 dump_queue_members(q); 03387 03388 res = RES_OKAY; 03389 } else { 03390 res = RES_EXISTS; 03391 } 03392 ast_mutex_unlock(&q->lock); 03393 break; 03394 } 03395 03396 if (res == RES_OKAY) 03397 remove_from_interfaces(interface); 03398 03399 AST_LIST_UNLOCK(&queues); 03400 03401 return res; 03402 }
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 1813 of file app_queue.c.
References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, ast_copy_string(), AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, compare_weight(), ast_channel::context, ast_channel::data, ast_channel::dialcontext, do_hang(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, member::interface, callattempt::interface, callattempt::lastcall, call_queue::lock, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, call_queue::ringinuse, call_queue::rrpos, member::status, callattempt::stillgoing, update_status(), vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.
Referenced by ring_one().
01814 { 01815 int res; 01816 int status; 01817 char tech[256]; 01818 char *location; 01819 const char *macrocontext, *macroexten; 01820 01821 /* on entry here, we know that tmp->chan == NULL */ 01822 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) { 01823 if (queue_debug) 01824 ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface); 01825 if (qe->chan->cdr) 01826 ast_cdr_busy(qe->chan->cdr); 01827 tmp->stillgoing = 0; 01828 (*busies)++; 01829 return 0; 01830 } 01831 01832 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 01833 if (queue_debug) 01834 ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface); 01835 if (qe->chan->cdr) 01836 ast_cdr_busy(qe->chan->cdr); 01837 tmp->stillgoing = 0; 01838 return 0; 01839 } 01840 01841 if (tmp->member->paused) { 01842 if (queue_debug) 01843 ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface); 01844 if (qe->chan->cdr) 01845 ast_cdr_busy(qe->chan->cdr); 01846 tmp->stillgoing = 0; 01847 return 0; 01848 } 01849 if (use_weight && compare_weight(qe->parent,tmp->member)) { 01850 if (queue_debug) 01851 ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 01852 if (qe->chan->cdr) 01853 ast_cdr_busy(qe->chan->cdr); 01854 tmp->stillgoing = 0; 01855 (*busies)++; 01856 return 0; 01857 } 01858 01859 ast_copy_string(tech, tmp->interface, sizeof(tech)); 01860 if ((location = strchr(tech, '/'))) 01861 *location++ = '\0'; 01862 else 01863 location = ""; 01864 01865 /* Request the peer */ 01866 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); 01867 if (!tmp->chan) { /* If we can't, just go on to the next call */ 01868 if (queue_debug) 01869 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech); 01870 if (qe->chan->cdr) 01871 ast_cdr_busy(qe->chan->cdr); 01872 tmp->stillgoing = 0; 01873 01874 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01875 01876 ast_mutex_lock(&qe->parent->lock); 01877 qe->parent->rrpos++; 01878 ast_mutex_unlock(&qe->parent->lock); 01879 01880 (*busies)++; 01881 return 0; 01882 } 01883 01884 /* Increment ring count */ 01885 tmp->member->ringcount++; 01886 tmp->chan->appl = "AppQueue"; 01887 tmp->chan->data = "(Outgoing Line)"; 01888 tmp->chan->whentohangup = 0; 01889 if (tmp->chan->cid.cid_num) 01890 free(tmp->chan->cid.cid_num); 01891 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); 01892 if (tmp->chan->cid.cid_name) 01893 free(tmp->chan->cid.cid_name); 01894 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); 01895 if (tmp->chan->cid.cid_ani) 01896 free(tmp->chan->cid.cid_ani); 01897 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); 01898 01899 /* Inherit specially named variables from parent channel */ 01900 ast_channel_inherit_variables(qe->chan, tmp->chan); 01901 01902 /* Presense of ADSI CPE on outgoing channel follows ours */ 01903 tmp->chan->adsicpe = qe->chan->adsicpe; 01904 01905 /* Inherit context and extension */ 01906 ast_channel_lock(qe->chan); 01907 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 01908 if (!ast_strlen_zero(macrocontext)) 01909 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext)); 01910 else 01911 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext)); 01912 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 01913 if (!ast_strlen_zero(macroexten)) 01914 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 01915 else 01916 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 01917 ast_channel_unlock(qe->chan); 01918 01919 /* Place the call, but don't wait on the answer */ 01920 if ((res = ast_call(tmp->chan, location, 0))) { 01921 /* Again, keep going even if there's an error */ 01922 if (option_debug) 01923 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); 01924 if (option_verbose > 2) 01925 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface); 01926 do_hang(tmp); 01927 (*busies)++; 01928 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01929 return 0; 01930 } else if (qe->parent->eventwhencalled) { 01931 char vars[2048]; 01932 01933 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 01934 "AgentCalled: %s\r\n" 01935 "AgentName: %s\r\n" 01936 "ChannelCalling: %s\r\n" 01937 "CallerID: %s\r\n" 01938 "CallerIDName: %s\r\n" 01939 "Context: %s\r\n" 01940 "Extension: %s\r\n" 01941 "Priority: %d\r\n" 01942 "%s", 01943 tmp->interface, tmp->member->membername, qe->chan->name, 01944 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", 01945 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", 01946 qe->chan->context, qe->chan->exten, qe->chan->priority, 01947 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 01948 if (option_verbose > 2) 01949 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface); 01950 } 01951 01952 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01953 return 1; 01954 }
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 1980 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().
01981 { 01982 int ret = 0; 01983 01984 while (ret == 0) { 01985 struct callattempt *best = find_best(outgoing); 01986 if (!best) { 01987 if (option_debug) 01988 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); 01989 break; 01990 } 01991 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 01992 struct callattempt *cur; 01993 /* Ring everyone who shares this best metric (for ringall) */ 01994 for (cur = outgoing; cur; cur = cur->q_next) { 01995 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 01996 if (option_debug) 01997 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 01998 ret |= ring_entry(qe, cur, busies); 01999 } 02000 } 02001 } else { 02002 /* Ring just the best channel */ 02003 if (option_debug) 02004 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric); 02005 ret = ring_entry(qe, best, busies); 02006 } 02007 } 02008 02009 return ret; 02010 }
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 2094 of file app_queue.c.
References ast_queue_log(), ast_verbose(), call_queue::autopause, queue_ent::chan, call_queue::name, option_verbose, queue_ent::parent, set_member_paused(), ast_channel::uniqueid, and VERBOSE_PREFIX_3.
02095 { 02096 if (option_verbose > 2) 02097 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime); 02098 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 02099 if (qe->parent->autopause && pause) { 02100 if (!set_member_paused(qe->parent->name, interface, 1)) { 02101 if (option_verbose > 2) 02102 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); 02103 } else { 02104 if (option_verbose > 2) 02105 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 02106 } 02107 } 02108 return; 02109 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3717 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_module_user::chan, ast_channel::context, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and ast_channel::uniqueid.
Referenced by load_module().
03718 { 03719 int res=-1; 03720 struct ast_module_user *lu; 03721 char *parse, *temppos = NULL; 03722 int priority_jump = 0; 03723 AST_DECLARE_APP_ARGS(args, 03724 AST_APP_ARG(queuename); 03725 AST_APP_ARG(interface); 03726 AST_APP_ARG(options); 03727 ); 03728 03729 03730 if (ast_strlen_zero(data)) { 03731 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n"); 03732 return -1; 03733 } 03734 03735 parse = ast_strdupa(data); 03736 03737 AST_STANDARD_APP_ARGS(args, parse); 03738 03739 lu = ast_module_user_add(chan); 03740 03741 if (ast_strlen_zero(args.interface)) { 03742 args.interface = ast_strdupa(chan->name); 03743 temppos = strrchr(args.interface, '-'); 03744 if (temppos) 03745 *temppos = '\0'; 03746 } 03747 03748 if (args.options) { 03749 if (strchr(args.options, 'j')) 03750 priority_jump = 1; 03751 } 03752 03753 switch (remove_from_queue(args.queuename, args.interface)) { 03754 case RES_OKAY: 03755 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 03756 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 03757 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 03758 res = 0; 03759 break; 03760 case RES_EXISTS: 03761 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 03762 if (priority_jump || ast_opt_priority_jumping) 03763 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 03764 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 03765 res = 0; 03766 break; 03767 case RES_NOSUCHQUEUE: 03768 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 03769 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 03770 res = 0; 03771 break; 03772 case RES_NOT_DYNAMIC: 03773 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 03774 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 03775 res = 0; 03776 break; 03777 } 03778 03779 ast_module_user_remove(lu); 03780 03781 return res; 03782 }
static void rr_dep_warning | ( | void | ) | [static] |
Definition at line 464 of file app_queue.c.
References ast_log(), and LOG_NOTICE.
Referenced by reload_queues().
00465 { 00466 static unsigned int warned = 0; 00467 00468 if (!warned) { 00469 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n"); 00470 warned = 1; 00471 } 00472 }
static void rt_handle_member_record | ( | struct call_queue * | q, | |
char * | interface, | |||
const char * | membername, | |||
const char * | penalty_str, | |||
const char * | paused_str | |||
) | [static] |
Definition at line 1124 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, and member::realtime.
Referenced by update_realtime_members().
01125 { 01126 struct member *m, tmpmem; 01127 int penalty = 0; 01128 int paused = 0; 01129 01130 if (penalty_str) { 01131 penalty = atoi(penalty_str); 01132 if (penalty < 0) 01133 penalty = 0; 01134 } 01135 01136 if (paused_str) { 01137 paused = atoi(paused_str); 01138 if (paused < 0) 01139 paused = 0; 01140 } 01141 01142 /* Find the member, or the place to put a new one. */ 01143 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 01144 m = ao2_find(q->members, &tmpmem, OBJ_POINTER); 01145 01146 /* Create a new one if not found, else update penalty */ 01147 if (!m) { 01148 if ((m = create_queue_member(interface, membername, penalty, paused))) { 01149 m->dead = 0; 01150 m->realtime = 1; 01151 add_to_interfaces(interface); 01152 ao2_link(q->members, m); 01153 ao2_ref(m, -1); 01154 m = NULL; 01155 q->membercount++; 01156 } 01157 } else { 01158 m->dead = 0; /* Do not delete this one. */ 01159 if (paused_str) 01160 m->paused = paused; 01161 m->penalty = penalty; 01162 ao2_ref(m, -1); 01163 } 01164 }
static int say_periodic_announcement | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 2036 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().
02037 { 02038 int res = 0; 02039 time_t now; 02040 02041 /* Get the current time */ 02042 time(&now); 02043 02044 /* Check to see if it is time to announce */ 02045 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 02046 return 0; 02047 02048 /* Stop the music on hold so we can play our own file */ 02049 ast_moh_stop(qe->chan); 02050 02051 if (option_verbose > 2) 02052 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n"); 02053 02054 /* Check to make sure we have a sound file. If not, reset to the first sound file */ 02055 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) { 02056 qe->last_periodic_announce_sound = 0; 02057 } 02058 02059 /* play the announcement */ 02060 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]); 02061 02062 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 02063 res = 0; 02064 02065 /* Resume Music on Hold if the caller is going to stay in the queue */ 02066 if (!res) 02067 ast_moh_start(qe->chan, qe->moh, NULL); 02068 02069 /* update last_periodic_announce_time */ 02070 qe->last_periodic_announce_time = now; 02071 02072 /* Update the current periodic announcement to the next announcement */ 02073 qe->last_periodic_announce_sound++; 02074 02075 return res; 02076 }
static int say_position | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1550 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, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, queue_ent::moh, ast_channel::name, call_queue::name, option_verbose, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, valid_exit(), and VERBOSE_PREFIX_3.
Referenced by queue_exec(), and wait_our_turn().
01551 { 01552 int res = 0, avgholdmins, avgholdsecs; 01553 time_t now; 01554 01555 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ 01556 time(&now); 01557 if ((now - qe->last_pos) < 15) 01558 return 0; 01559 01560 /* If either our position has changed, or we are over the freq timer, say position */ 01561 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 01562 return 0; 01563 01564 ast_moh_stop(qe->chan); 01565 /* Say we're next, if we are */ 01566 if (qe->pos == 1) { 01567 res = play_file(qe->chan, qe->parent->sound_next); 01568 if (res) 01569 goto playout; 01570 else 01571 goto posout; 01572 } else { 01573 res = play_file(qe->chan, qe->parent->sound_thereare); 01574 if (res) 01575 goto playout; 01576 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */ 01577 if (res) 01578 goto playout; 01579 res = play_file(qe->chan, qe->parent->sound_calls); 01580 if (res) 01581 goto playout; 01582 } 01583 /* Round hold time to nearest minute */ 01584 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 01585 01586 /* If they have specified a rounding then round the seconds as well */ 01587 if (qe->parent->roundingseconds) { 01588 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 01589 avgholdsecs *= qe->parent->roundingseconds; 01590 } else { 01591 avgholdsecs = 0; 01592 } 01593 01594 if (option_verbose > 2) 01595 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 01596 01597 /* If the hold time is >1 min, if it's enabled, and if it's not 01598 supposed to be only once and we have already said it, say it */ 01599 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 01600 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 01601 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 01602 res = play_file(qe->chan, qe->parent->sound_holdtime); 01603 if (res) 01604 goto playout; 01605 01606 if (avgholdmins > 0) { 01607 if (avgholdmins < 2) { 01608 res = play_file(qe->chan, qe->parent->sound_lessthan); 01609 if (res) 01610 goto playout; 01611 01612 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL); 01613 if (res) 01614 goto playout; 01615 } else { 01616 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 01617 if (res) 01618 goto playout; 01619 } 01620 01621 res = play_file(qe->chan, qe->parent->sound_minutes); 01622 if (res) 01623 goto playout; 01624 } 01625 if (avgholdsecs>0) { 01626 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 01627 if (res) 01628 goto playout; 01629 01630 res = play_file(qe->chan, qe->parent->sound_seconds); 01631 if (res) 01632 goto playout; 01633 } 01634 01635 } 01636 01637 posout: 01638 if (option_verbose > 2) 01639 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", 01640 qe->chan->name, qe->parent->name, qe->pos); 01641 res = play_file(qe->chan, qe->parent->sound_thanks); 01642 01643 playout: 01644 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 01645 res = 0; 01646 01647 /* Set our last_pos indicators */ 01648 qe->last_pos = now; 01649 qe->last_pos_said = qe->pos; 01650 01651 /* Don't restart music on hold if we're about to exit the caller from the queue */ 01652 if (!res) 01653 ast_moh_start(qe->chan, qe->moh, NULL); 01654 01655 return res; 01656 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
int | paused | |||
) | [static] |
Definition at line 3460 of file app_queue.c.
References ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), call_queue::lock, 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().
03461 { 03462 int found = 0; 03463 struct call_queue *q; 03464 struct member *mem; 03465 03466 /* Special event for when all queues are paused - individual events still generated */ 03467 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 03468 if (ast_strlen_zero(queuename)) 03469 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 03470 03471 AST_LIST_LOCK(&queues); 03472 AST_LIST_TRAVERSE(&queues, q, list) { 03473 ast_mutex_lock(&q->lock); 03474 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 03475 if ((mem = interface_exists(q, interface))) { 03476 found++; 03477 if (mem->paused == paused) 03478 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 03479 mem->paused = paused; 03480 03481 if (queue_persistent_members) 03482 dump_queue_members(q); 03483 03484 if (mem->realtime) 03485 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 03486 03487 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", ""); 03488 03489 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 03490 "Queue: %s\r\n" 03491 "Location: %s\r\n" 03492 "MemberName: %s\r\n" 03493 "Paused: %d\r\n", 03494 q->name, mem->interface, mem->membername, paused); 03495 ao2_ref(mem, -1); 03496 } 03497 } 03498 ast_mutex_unlock(&q->lock); 03499 } 03500 AST_LIST_UNLOCK(&queues); 03501 03502 return found ? RESULT_SUCCESS : RESULT_FAILURE; 03503 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 483 of file app_queue.c.
References queue_ent::chan, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
00484 { 00485 int i; 00486 00487 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) { 00488 if (queue_results[i].id == res) { 00489 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 00490 return; 00491 } 00492 } 00493 }
static struct ast_datastore* setup_transfer_datastore | ( | struct queue_ent * | qe, | |
struct member * | member, | |||
time_t | starttime, | |||
int | callcompletedinsl | |||
) | [static] |
create a datastore for storing relevant info to log attended transfers in the queue_log
Definition at line 2676 of file app_queue.c.
References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_lock, ast_channel_unlock, ast_log(), queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, and queue_transfer_info.
Referenced by try_calling().
02677 { 02678 struct ast_datastore *ds; 02679 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 02680 02681 if (!qtds) { 02682 ast_log(LOG_WARNING, "Memory allocation error!\n"); 02683 return NULL; 02684 } 02685 02686 ast_channel_lock(qe->chan); 02687 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) { 02688 ast_channel_unlock(qe->chan); 02689 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 02690 return NULL; 02691 } 02692 02693 qtds->qe = qe; 02694 /* This member is refcounted in try_calling, so no need to add it here, too */ 02695 qtds->member = member; 02696 qtds->starttime = starttime; 02697 qtds->callcompletedinsl = callcompletedinsl; 02698 ds->data = qtds; 02699 ast_channel_datastore_add(qe->chan, ds); 02700 ast_channel_unlock(qe->chan); 02701 return ds; 02702 }
static int statechange_queue | ( | const char * | dev, | |
int | state, | |||
void * | ign | |||
) | [static] |
Producer of the statechange queue.
Definition at line 744 of file app_queue.c.
References ast_calloc, ast_cond_signal(), AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), device_state, and statechange::entry.
Referenced by load_module(), and unload_module().
00745 { 00746 struct statechange *sc; 00747 00748 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1))) 00749 return 0; 00750 00751 sc->state = state; 00752 strcpy(sc->dev, dev); 00753 00754 ast_mutex_lock(&device_state.lock); 00755 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry); 00756 ast_cond_signal(&device_state.cond); 00757 ast_mutex_unlock(&device_state.lock); 00758 00759 return 0; 00760 }
static int store_next | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing | |||
) | [static] |
Definition at line 2012 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().
02013 { 02014 struct callattempt *best = find_best(outgoing); 02015 02016 if (best) { 02017 /* Ring just the best channel */ 02018 if (option_debug) 02019 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric); 02020 qe->parent->rrpos = best->metric % 1000; 02021 } else { 02022 /* Just increment rrpos */ 02023 if (qe->parent->wrapped) { 02024 /* No more channels, start over */ 02025 qe->parent->rrpos = 0; 02026 } else { 02027 /* Prioritize next entry */ 02028 qe->parent->rrpos++; 02029 } 02030 } 02031 qe->parent->wrapped = 0; 02032 02033 return 0; 02034 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 507 of file app_queue.c.
References name, and strategies.
Referenced by queue_set_param().
00508 { 00509 int x; 00510 00511 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) { 00512 if (!strcasecmp(strategy, strategies[x].name)) 00513 return strategies[x].strategy; 00514 } 00515 00516 return -1; 00517 }
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 2729 of file app_queue.c.
References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), app, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CDR_FLAG_DONT_TOUCH, AST_CDR_FLAG_LOCKED, 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_mutex_lock(), ast_mutex_unlock(), 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, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, member::interface, ast_channel::language, member::lastcall, leave_queue(), ast_dialed_interface::list, call_queue::lock, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, ast_channel::name, 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, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, queue_transfer_info, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, call_queue::servicelevel, call_queue::setinterfacevar, setup_transfer_datastore(), call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, store_next(), call_queue::strategy, tds, ast_channel::tech, call_queue::timeout, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, ast_channel::uniqueid, update_queue(), vars2manager(), and wait_for_answer().
Referenced by queue_exec().
02730 { 02731 struct member *cur; 02732 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 02733 int to; 02734 char oldexten[AST_MAX_EXTENSION]=""; 02735 char oldcontext[AST_MAX_CONTEXT]=""; 02736 char queuename[256]=""; 02737 struct ast_channel *peer; 02738 struct ast_channel *which; 02739 struct callattempt *lpeer; 02740 struct member *member; 02741 struct ast_app *app; 02742 int res = 0, bridge = 0; 02743 int numbusies = 0; 02744 int x=0; 02745 char *announce = NULL; 02746 char digit = 0; 02747 time_t callstart; 02748 time_t now = time(NULL); 02749 struct ast_bridge_config bridge_config; 02750 char nondataquality = 1; 02751 char *agiexec = NULL; 02752 int ret = 0; 02753 const char *monitorfilename; 02754 const char *monitor_exec; 02755 const char *monitor_options; 02756 char tmpid[256], tmpid2[256]; 02757 char meid[1024], meid2[1024]; 02758 char mixmonargs[1512]; 02759 struct ast_app *mixmonapp = NULL; 02760 char *p; 02761 char vars[2048]; 02762 int forwardsallowed = 1; 02763 int callcompletedinsl; 02764 struct ao2_iterator memi; 02765 struct ast_datastore *datastore, *transfer_ds; 02766 02767 ast_channel_lock(qe->chan); 02768 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 02769 ast_channel_unlock(qe->chan); 02770 02771 memset(&bridge_config, 0, sizeof(bridge_config)); 02772 time(&now); 02773 02774 /* If we've already exceeded our timeout, then just stop 02775 * This should be extremely rare. queue_exec will take care 02776 * of removing the caller and reporting the timeout as the reason. 02777 */ 02778 if (qe->expire && now >= qe->expire) { 02779 res = 0; 02780 goto out; 02781 } 02782 02783 for (; options && *options; options++) 02784 switch (*options) { 02785 case 't': 02786 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 02787 break; 02788 case 'T': 02789 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 02790 break; 02791 case 'w': 02792 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 02793 break; 02794 case 'W': 02795 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 02796 break; 02797 case 'd': 02798 nondataquality = 0; 02799 break; 02800 case 'h': 02801 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 02802 break; 02803 case 'H': 02804 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 02805 break; 02806 case 'n': 02807 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) 02808 (*tries)++; 02809 else 02810 *tries = qe->parent->membercount; 02811 *noption = 1; 02812 break; 02813 case 'i': 02814 forwardsallowed = 0; 02815 break; 02816 } 02817 02818 /* Hold the lock while we setup the outgoing calls */ 02819 if (use_weight) 02820 AST_LIST_LOCK(&queues); 02821 ast_mutex_lock(&qe->parent->lock); 02822 if (option_debug) 02823 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 02824 qe->chan->name); 02825 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 02826 if (!ast_strlen_zero(qe->announce)) 02827 announce = qe->announce; 02828 if (!ast_strlen_zero(announceoverride)) 02829 announce = announceoverride; 02830 02831 memi = ao2_iterator_init(qe->parent->members, 0); 02832 while ((cur = ao2_iterator_next(&memi))) { 02833 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 02834 struct ast_dialed_interface *di; 02835 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 02836 if (!tmp) { 02837 ao2_ref(cur, -1); 02838 ast_mutex_unlock(&qe->parent->lock); 02839 if (use_weight) 02840 AST_LIST_UNLOCK(&queues); 02841 goto out; 02842 } 02843 if (!datastore) { 02844 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) { 02845 ao2_ref(cur, -1); 02846 ast_mutex_unlock(&qe->parent->lock); 02847 if (use_weight) 02848 AST_LIST_UNLOCK(&queues); 02849 free(tmp); 02850 goto out; 02851 } 02852 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 02853 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 02854 ao2_ref(cur, -1); 02855 ast_mutex_unlock(&qe->parent->lock); 02856 if (use_weight) 02857 AST_LIST_UNLOCK(&queues); 02858 free(tmp); 02859 goto out; 02860 } 02861 datastore->data = dialed_interfaces; 02862 AST_LIST_HEAD_INIT(dialed_interfaces); 02863 02864 ast_channel_lock(qe->chan); 02865 ast_channel_datastore_add(qe->chan, datastore); 02866 ast_channel_unlock(qe->chan); 02867 } else 02868 dialed_interfaces = datastore->data; 02869 02870 AST_LIST_LOCK(dialed_interfaces); 02871 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 02872 if (!strcasecmp(cur->interface, di->interface)) { 02873 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 02874 di->interface); 02875 break; 02876 } 02877 } 02878 AST_LIST_UNLOCK(dialed_interfaces); 02879 02880 if (di) { 02881 free(tmp); 02882 continue; 02883 } 02884 02885 /* It is always ok to dial a Local interface. We only keep track of 02886 * which "real" interfaces have been dialed. The Local channel will 02887 * inherit this list so that if it ends up dialing a real interface, 02888 * it won't call one that has already been called. */ 02889 if (strncasecmp(cur->interface, "Local/", 6)) { 02890 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 02891 ao2_ref(cur, -1); 02892 ast_mutex_unlock(&qe->parent->lock); 02893 if (use_weight) 02894 AST_LIST_UNLOCK(&queues); 02895 free(tmp); 02896 goto out; 02897 } 02898 strcpy(di->interface, cur->interface); 02899 02900 AST_LIST_LOCK(dialed_interfaces); 02901 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 02902 AST_LIST_UNLOCK(dialed_interfaces); 02903 } 02904 02905 tmp->stillgoing = -1; 02906 tmp->member = cur; 02907 tmp->oldstatus = cur->status; 02908 tmp->lastcall = cur->lastcall; 02909 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 02910 if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) { 02911 cur->ringcount = 0; 02912 } 02913 /* Special case: If we ring everyone, go ahead and ring them, otherwise 02914 just calculate their metric for the appropriate strategy */ 02915 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 02916 /* Put them in the list of outgoing thingies... We're ready now. 02917 XXX If we're forcibly removed, these outgoing calls won't get 02918 hung up XXX */ 02919 tmp->q_next = outgoing; 02920 outgoing = tmp; 02921 /* If this line is up, don't try anybody else */ 02922 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 02923 break; 02924 } else { 02925 ao2_ref(cur, -1); 02926 free(tmp); 02927 } 02928 } 02929 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 02930 to = (qe->expire - now) * 1000; 02931 else 02932 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 02933 ++qe->pending; 02934 ++qe->tries; 02935 if (option_debug) 02936 ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n", 02937 qe->chan->name, queuename, qe->tries); 02938 ast_mutex_unlock(&qe->parent->lock); 02939 ring_one(qe, outgoing, &numbusies); 02940 if (use_weight) 02941 AST_LIST_UNLOCK(&queues); 02942 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); 02943 /* The ast_channel_datastore_remove() function could fail here if the 02944 * datastore was moved to another channel during a masquerade. If this is 02945 * the case, don't free the datastore here because later, when the channel 02946 * to which the datastore was moved hangs up, it will attempt to free this 02947 * datastore again, causing a crash 02948 */ 02949 ast_channel_lock(qe->chan); 02950 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 02951 ast_channel_datastore_free(datastore); 02952 } 02953 ast_channel_unlock(qe->chan); 02954 ast_mutex_lock(&qe->parent->lock); 02955 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { 02956 store_next(qe, outgoing); 02957 } 02958 ast_mutex_unlock(&qe->parent->lock); 02959 peer = lpeer ? lpeer->chan : NULL; 02960 if (!peer) { 02961 qe->pending = 0; 02962 if (to) { 02963 /* Must gotten hung up */ 02964 res = -1; 02965 } else { 02966 /* User exited by pressing a digit */ 02967 res = digit; 02968 } 02969 if (res == -1) { 02970 /* Post this CDR, and mark call as NOANSWER */ 02971 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH); 02972 ast_cdr_noanswer(qe->chan->cdr); 02973 if (queue_debug) 02974 ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name); 02975 } 02976 if (qe->parent->eventwhencalled) { 02977 manager_event(EVENT_FLAG_AGENT, "AgentTimeout", 02978 "Queue: %s\r\n" 02979 "ChannelCalling: %s\r\n" 02980 "Uniqueid: %s\r\n" 02981 "Tries: %d\r\n" 02982 "Holdtime: %ld\r\n", 02983 queuename, qe->chan->name, qe->chan->uniqueid, qe->tries, 02984 (long)time(NULL) - qe->start); 02985 } 02986 } else { /* peer is valid */ 02987 /* Ah ha! Someone answered within the desired timeframe. Of course after this 02988 we will always return with -1 so that it is hung up properly after the 02989 conversation. */ 02990 if (!strcmp(qe->chan->tech->type, "Zap")) 02991 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 02992 if (!strcmp(peer->tech->type, "Zap")) 02993 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 02994 /* Update parameters for the queue */ 02995 time(&now); 02996 recalc_holdtime(qe, (now - qe->start)); 02997 ast_mutex_lock(&qe->parent->lock); 02998 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 02999 ast_mutex_unlock(&qe->parent->lock); 03000 member = lpeer->member; 03001 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 03002 ao2_ref(member, 1); 03003 hangupcalls(outgoing, peer); 03004 outgoing = NULL; 03005 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 03006 int res2; 03007 03008 res2 = ast_autoservice_start(qe->chan); 03009 if (!res2) { 03010 if (qe->parent->memberdelay) { 03011 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 03012 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 03013 } 03014 if (!res2 && announce) { 03015 play_file(peer, announce); 03016 } 03017 if (!res2 && qe->parent->reportholdtime) { 03018 if (!play_file(peer, qe->parent->sound_reporthold)) { 03019 int holdtime; 03020 03021 time(&now); 03022 holdtime = abs((now - qe->start) / 60); 03023 if (holdtime < 2) { 03024 play_file(peer, qe->parent->sound_lessthan); 03025 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL); 03026 } else 03027 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 03028 play_file(peer, qe->parent->sound_minutes); 03029 } 03030 } 03031 } 03032 res2 |= ast_autoservice_stop(qe->chan); 03033 if (peer->_softhangup) { 03034 /* Agent must have hung up */ 03035 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 03036 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 03037 if (qe->parent->eventwhencalled) 03038 manager_event(EVENT_FLAG_AGENT, "AgentDump", 03039 "Queue: %s\r\n" 03040 "Uniqueid: %s\r\n" 03041 "Channel: %s\r\n" 03042 "Member: %s\r\n" 03043 "MemberName: %s\r\n" 03044 "%s", 03045 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03046 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03047 ast_hangup(peer); 03048 ao2_ref(member, -1); 03049 goto out; 03050 } else if (res2) { 03051 /* Caller must have hung up just before being connected*/ 03052 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 03053 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 03054 record_abandoned(qe); 03055 ast_hangup(peer); 03056 ao2_ref(member, -1); 03057 return -1; 03058 } 03059 } 03060 /* Stop music on hold */ 03061 ast_moh_stop(qe->chan); 03062 /* If appropriate, log that we have a destination channel */ 03063 if (qe->chan->cdr) 03064 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 03065 /* Make sure channels are compatible */ 03066 res = ast_channel_make_compatible(qe->chan, peer); 03067 if (res < 0) { 03068 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 03069 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 03070 record_abandoned(qe); 03071 ast_hangup(peer); 03072 ao2_ref(member, -1); 03073 return -1; 03074 } 03075 03076 if (qe->parent->setinterfacevar) 03077 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface); 03078 03079 /* Begin Monitoring */ 03080 if (qe->parent->monfmt && *qe->parent->monfmt) { 03081 if (!qe->parent->montype) { 03082 if (option_debug) 03083 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n"); 03084 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 03085 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) 03086 which = qe->chan; 03087 else 03088 which = peer; 03089 if (monitorfilename) 03090 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); 03091 else if (qe->chan->cdr) 03092 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); 03093 else { 03094 /* Last ditch effort -- no CDR, make up something */ 03095 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03096 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 ); 03097 } 03098 if (qe->parent->monjoin) 03099 ast_monitor_setjoinfiles(which, 1); 03100 } else { 03101 if (option_debug) 03102 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n"); 03103 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 03104 if (!monitorfilename) { 03105 if (qe->chan->cdr) 03106 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1); 03107 else 03108 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03109 } else { 03110 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1); 03111 for (p = tmpid2; *p ; p++) { 03112 if (*p == '^' && *(p+1) == '{') { 03113 *p = '$'; 03114 } 03115 } 03116 03117 memset(tmpid, 0, sizeof(tmpid)); 03118 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 03119 } 03120 03121 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"); 03122 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"); 03123 03124 if (monitor_exec) { 03125 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1); 03126 for (p = meid2; *p ; p++) { 03127 if (*p == '^' && *(p+1) == '{') { 03128 *p = '$'; 03129 } 03130 } 03131 03132 memset(meid, 0, sizeof(meid)); 03133 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 03134 } 03135 03136 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt); 03137 03138 mixmonapp = pbx_findapp("MixMonitor"); 03139 03140 if (strchr(tmpid2, '|')) { 03141 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n"); 03142 mixmonapp = NULL; 03143 } 03144 03145 if (!monitor_options) 03146 monitor_options = ""; 03147 03148 if (strchr(monitor_options, '|')) { 03149 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n"); 03150 mixmonapp = NULL; 03151 } 03152 03153 if (mixmonapp) { 03154 if (!ast_strlen_zero(monitor_exec)) 03155 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec); 03156 else 03157 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options); 03158 03159 if (option_debug) 03160 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 03161 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 03162 if (qe->chan->cdr) 03163 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03164 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 03165 if (qe->chan->cdr) 03166 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03167 03168 } else 03169 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 03170 03171 } 03172 } 03173 /* Drop out of the queue at this point, to prepare for next caller */ 03174 leave_queue(qe); 03175 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 03176 if (option_debug) 03177 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); 03178 ast_channel_sendurl(peer, url); 03179 } 03180 if (!ast_strlen_zero(agi)) { 03181 if (option_debug) 03182 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi); 03183 app = pbx_findapp("agi"); 03184 if (app) { 03185 agiexec = ast_strdupa(agi); 03186 ret = pbx_exec(qe->chan, app, agiexec); 03187 } else 03188 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 03189 } 03190 qe->handled++; 03191 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid); 03192 if (qe->parent->eventwhencalled) 03193 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 03194 "Queue: %s\r\n" 03195 "Uniqueid: %s\r\n" 03196 "Channel: %s\r\n" 03197 "Member: %s\r\n" 03198 "MemberName: %s\r\n" 03199 "Holdtime: %ld\r\n" 03200 "BridgedChannel: %s\r\n" 03201 "%s", 03202 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03203 (long)time(NULL) - qe->start, peer->uniqueid, 03204 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03205 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 03206 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 03207 time(&callstart); 03208 03209 if (member->status == AST_DEVICE_NOT_INUSE) 03210 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); 03211 03212 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 03213 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 03214 03215 ast_channel_lock(qe->chan); 03216 if (!attended_transfer_occurred(qe->chan)) { 03217 struct ast_datastore *tds; 03218 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) { 03219 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 03220 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 03221 (long) (time(NULL) - callstart)); 03222 } else if (qe->chan->_softhangup) { 03223 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 03224 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03225 if (qe->parent->eventwhencalled) 03226 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03227 "Queue: %s\r\n" 03228 "Uniqueid: %s\r\n" 03229 "Channel: %s\r\n" 03230 "Member: %s\r\n" 03231 "MemberName: %s\r\n" 03232 "HoldTime: %ld\r\n" 03233 "TalkTime: %ld\r\n" 03234 "Reason: caller\r\n" 03235 "%s", 03236 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03237 (long)(callstart - qe->start), (long)(time(NULL) - callstart), 03238 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03239 } else { 03240 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 03241 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03242 if (qe->parent->eventwhencalled) 03243 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03244 "Queue: %s\r\n" 03245 "Uniqueid: %s\r\n" 03246 "Channel: %s\r\n" 03247 "MemberName: %s\r\n" 03248 "HoldTime: %ld\r\n" 03249 "TalkTime: %ld\r\n" 03250 "Reason: agent\r\n" 03251 "%s", 03252 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start), 03253 (long)(time(NULL) - callstart), 03254 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03255 } 03256 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 03257 ast_channel_datastore_remove(qe->chan, tds); 03258 } 03259 update_queue(qe->parent, member, callcompletedinsl); 03260 } 03261 03262 if (transfer_ds) { 03263 ast_channel_datastore_free(transfer_ds); 03264 } 03265 ast_channel_unlock(qe->chan); 03266 ast_hangup(peer); 03267 res = bridge ? bridge : 1; 03268 ao2_ref(member, -1); 03269 } 03270 out: 03271 hangupcalls(outgoing, NULL); 03272 03273 return res; 03274 }
static int unload_module | ( | void | ) | [static] |
Definition at line 5186 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, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, and statechange_queue().
05187 { 05188 int res; 05189 05190 if (device_state.thread != AST_PTHREADT_NULL) { 05191 device_state.stop = 1; 05192 ast_mutex_lock(&device_state.lock); 05193 ast_cond_signal(&device_state.cond); 05194 ast_mutex_unlock(&device_state.lock); 05195 pthread_join(device_state.thread, NULL); 05196 } 05197 05198 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 05199 res = ast_manager_unregister("QueueStatus"); 05200 res |= ast_manager_unregister("Queues"); 05201 res |= ast_manager_unregister("QueueAdd"); 05202 res |= ast_manager_unregister("QueueRemove"); 05203 res |= ast_manager_unregister("QueuePause"); 05204 res |= ast_unregister_application(app_aqm); 05205 res |= ast_unregister_application(app_rqm); 05206 res |= ast_unregister_application(app_pqm); 05207 res |= ast_unregister_application(app_upqm); 05208 res |= ast_unregister_application(app_ql); 05209 res |= ast_unregister_application(app); 05210 res |= ast_custom_function_unregister(&queueagentcount_function); 05211 res |= ast_custom_function_unregister(&queuemembercount_function); 05212 res |= ast_custom_function_unregister(&queuememberlist_function); 05213 res |= ast_custom_function_unregister(&queuewaitingcount_function); 05214 ast_devstate_del(statechange_queue, NULL); 05215 05216 ast_module_user_hangup_all(); 05217 05218 clear_and_free_interfaces(); 05219 05220 return res; 05221 }
static int update_queue | ( | struct call_queue * | q, | |
struct member * | member, | |||
int | callcompletedinsl | |||
) | [static] |
Definition at line 2529 of file app_queue.c.
References ast_mutex_lock(), ast_mutex_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, and call_queue::lock.
Referenced by queue_transfer_fixup(), and try_calling().
02530 { 02531 ast_mutex_lock(&q->lock); 02532 time(&member->lastcall); 02533 member->calls++; 02534 q->callscompleted++; 02535 if (callcompletedinsl) 02536 q->callscompletedinsl++; 02537 ast_mutex_unlock(&q->lock); 02538 return 0; 02539 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 1315 of file app_queue.c.
References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), ast_variables_destroy(), member::interface, and var.
Referenced by set_member_paused().
01316 { 01317 struct ast_variable *var, *save; 01318 int ret = -1; 01319 01320 if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 01321 return ret; 01322 save = var; 01323 while (var) { 01324 if (!strcmp(var->name, "uniqueid")) 01325 break; 01326 var = var->next; 01327 } 01328 if (var && !ast_strlen_zero(var->value)) { 01329 if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1) 01330 ret = 0; 01331 } 01332 ast_variables_destroy(save); 01333 return ret; 01334 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 1336 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), ast_category_browse(), ast_config_destroy(), ast_load_realtime_multientry(), ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_variable_retrieve(), member::dead, member::interface, member_interface::interface, call_queue::lock, LOG_DEBUG, call_queue::membercount, call_queue::members, call_queue::name, option_debug, member::realtime, remove_from_interfaces(), rt_handle_member_record(), and S_OR.
Referenced by load_realtime_queue(), and queue_exec().
01337 { 01338 struct ast_config *member_config = NULL; 01339 struct member *m; 01340 char *interface = NULL; 01341 struct ao2_iterator mem_iter; 01342 01343 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) { 01344 /*This queue doesn't have realtime members*/ 01345 if (option_debug > 2) 01346 ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name); 01347 return; 01348 } 01349 01350 ast_mutex_lock(&q->lock); 01351 01352 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 01353 mem_iter = ao2_iterator_init(q->members, 0); 01354 while ((m = ao2_iterator_next(&mem_iter))) { 01355 if (m->realtime) 01356 m->dead = 1; 01357 ao2_ref(m, -1); 01358 } 01359 01360 while ((interface = ast_category_browse(member_config, interface))) { 01361 rt_handle_member_record(q, interface, 01362 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 01363 ast_variable_retrieve(member_config, interface, "penalty"), 01364 ast_variable_retrieve(member_config, interface, "paused")); 01365 } 01366 01367 /* Delete all realtime members that have been deleted in DB. */ 01368 mem_iter = ao2_iterator_init(q->members, 0); 01369 while ((m = ao2_iterator_next(&mem_iter))) { 01370 if (m->dead) { 01371 ao2_unlink(q->members, m); 01372 ast_mutex_unlock(&q->lock); 01373 remove_from_interfaces(m->interface); 01374 ast_mutex_lock(&q->lock); 01375 q->membercount--; 01376 } 01377 ao2_ref(m, -1); 01378 } 01379 ast_mutex_unlock(&q->lock); 01380 ast_config_destroy(member_config); 01381 }
static int update_status | ( | const char * | interface, | |
const int | status | |||
) | [static] |
Definition at line 596 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, member_interface::list, call_queue::lock, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, and member::status.
Referenced by handle_statechange(), and ring_entry().
00597 { 00598 struct member *cur; 00599 struct ao2_iterator mem_iter; 00600 struct call_queue *q; 00601 00602 AST_LIST_LOCK(&queues); 00603 AST_LIST_TRAVERSE(&queues, q, list) { 00604 ast_mutex_lock(&q->lock); 00605 mem_iter = ao2_iterator_init(q->members, 0); 00606 while ((cur = ao2_iterator_next(&mem_iter))) { 00607 char *tmp_interface; 00608 char *slash_pos; 00609 tmp_interface = ast_strdupa(cur->interface); 00610 if ((slash_pos = strchr(tmp_interface, '/'))) 00611 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00612 *slash_pos = '\0'; 00613 00614 if (strcasecmp(interface, tmp_interface)) { 00615 ao2_ref(cur, -1); 00616 continue; 00617 } 00618 00619 if (cur->status != status) { 00620 cur->status = status; 00621 if (q->maskmemberstatus) { 00622 ao2_ref(cur, -1); 00623 continue; 00624 } 00625 00626 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 00627 "Queue: %s\r\n" 00628 "Location: %s\r\n" 00629 "MemberName: %s\r\n" 00630 "Membership: %s\r\n" 00631 "Penalty: %d\r\n" 00632 "CallsTaken: %d\r\n" 00633 "LastCall: %d\r\n" 00634 "Status: %d\r\n" 00635 "Paused: %d\r\n", 00636 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", 00637 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); 00638 } 00639 ao2_ref(cur, -1); 00640 } 00641 ast_mutex_unlock(&q->lock); 00642 } 00643 AST_LIST_UNLOCK(&queues); 00644 00645 return 0; 00646 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3659 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_module_user::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().
Referenced by load_module().
03660 { 03661 struct ast_module_user *lu; 03662 char *parse; 03663 int priority_jump = 0; 03664 int ignore_fail = 0; 03665 AST_DECLARE_APP_ARGS(args, 03666 AST_APP_ARG(queuename); 03667 AST_APP_ARG(interface); 03668 AST_APP_ARG(options); 03669 ); 03670 03671 if (ast_strlen_zero(data)) { 03672 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03673 return -1; 03674 } 03675 03676 parse = ast_strdupa(data); 03677 03678 AST_STANDARD_APP_ARGS(args, parse); 03679 03680 lu = ast_module_user_add(chan); 03681 03682 if (args.options) { 03683 if (strchr(args.options, 'j')) 03684 priority_jump = 1; 03685 if (strchr(args.options, 'i')) 03686 ignore_fail = 1; 03687 } 03688 03689 if (ast_strlen_zero(args.interface)) { 03690 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03691 ast_module_user_remove(lu); 03692 return -1; 03693 } 03694 03695 if (set_member_paused(args.queuename, args.interface, 0)) { 03696 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 03697 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 03698 if (priority_jump || ast_opt_priority_jumping) { 03699 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03700 ast_module_user_remove(lu); 03701 return 0; 03702 } 03703 } 03704 ast_module_user_remove(lu); 03705 if (ignore_fail) { 03706 return 0; 03707 } else { 03708 return -1; 03709 } 03710 } 03711 03712 ast_module_user_remove(lu); 03713 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 03714 return 0; 03715 }
static int valid_exit | ( | struct queue_ent * | qe, | |
char | digit | |||
) | [static] |
Definition at line 1517 of file app_queue.c.
References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::context, queue_ent::digits, and queue_ent::valid_digits.
Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), and wait_our_turn().
01518 { 01519 int digitlen = strlen(qe->digits); 01520 01521 /* Prevent possible buffer overflow */ 01522 if (digitlen < sizeof(qe->digits) - 2) { 01523 qe->digits[digitlen] = digit; 01524 qe->digits[digitlen + 1] = '\0'; 01525 } else { 01526 qe->digits[0] = '\0'; 01527 return 0; 01528 } 01529 01530 /* If there's no context to goto, short-circuit */ 01531 if (ast_strlen_zero(qe->context)) 01532 return 0; 01533 01534 /* If the extension is bad, then reset the digits to blank */ 01535 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) { 01536 qe->digits[0] = '\0'; 01537 return 0; 01538 } 01539 01540 /* We have an exact match */ 01541 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 01542 qe->valid_digits = 1; 01543 /* Return 1 on a successful goto */ 01544 return 1; 01545 } 01546 01547 return 0; 01548 }
static char* vars2manager | ( | struct ast_channel * | chan, | |
char * | vars, | |||
size_t | len | |||
) | [static] |
Definition at line 1773 of file app_queue.c.
References ast_copy_string(), and pbx_builtin_serialize_variables().
Referenced by ring_entry(), and try_calling().
01774 { 01775 char *tmp = alloca(len); 01776 01777 if (pbx_builtin_serialize_variables(chan, tmp, len)) { 01778 int i, j; 01779 01780 /* convert "\n" to "\nVariable: " */ 01781 strcpy(vars, "Variable: "); 01782 01783 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 01784 vars[j] = tmp[i]; 01785 01786 if (tmp[i + 1] == '\0') 01787 break; 01788 if (tmp[i] == '\n') { 01789 vars[j++] = '\r'; 01790 vars[j++] = '\n'; 01791 01792 ast_copy_string(&(vars[j]), "Variable: ", len - j); 01793 j += 9; 01794 } 01795 } 01796 if (j > len - 3) 01797 j = len - 3; 01798 vars[j++] = '\r'; 01799 vars[j++] = '\n'; 01800 vars[j] = '\0'; 01801 } else { 01802 /* there are no channel variables; leave it blank */ 01803 *vars = '\0'; 01804 } 01805 return vars; 01806 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 3276 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
03277 { 03278 /* Don't need to hold the lock while we setup the outgoing calls */ 03279 int retrywait = qe->parent->retry * 1000; 03280 03281 int res = ast_waitfordigit(qe->chan, retrywait); 03282 if (res > 0 && !valid_exit(qe, res)) 03283 res = 0; 03284 03285 return res; 03286 }
static struct callattempt* wait_for_answer | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing, | |||
int * | to, | |||
char * | digit, | |||
int | prebusies, | |||
int | caller_disconnect, | |||
int | forwardsallowed | |||
) | [static] |
Wait for a member to answer the call.
[in] | qe | the queue_ent corresponding to the caller in the queue |
[in] | outgoing | the list of callattempts. Relevant ones will have their chan and stillgoing parameters non-zero |
[in] | to | the amount of time (in milliseconds) to wait for a response |
[out] | digit | if a user presses a digit to exit the queue, this is the digit the caller pressed |
[in] | prebusies | number of busy members calculated prior to calling wait_for_answer |
[in] | caller_disconnect | if the 'H' option is used when calling Queue(), this is used to detect if the caller pressed * to disconnect the call |
[in] | forwardsallowed | used to detect if we should allow call forwarding, based on the 'i' option to Queue() |
Definition at line 2122 of file app_queue.c.
References AST_MAX_WATCHERS, callattempt::call_next, callattempt::chan, queue_ent::chan, f, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, callattempt::stillgoing, and call_queue::strategy.
02123 { 02124 char *queue = qe->parent->name; 02125 struct callattempt *o, *start = NULL, *prev = NULL; 02126 int status; 02127 int numbusies = prebusies; 02128 int numnochan = 0; 02129 int stillgoing = 0; 02130 int orig = *to; 02131 struct ast_frame *f; 02132 struct callattempt *peer = NULL; 02133 struct ast_channel *winner; 02134 struct ast_channel *in = qe->chan; 02135 char on[80] = ""; 02136 char membername[80] = ""; 02137 long starttime = 0; 02138 long endtime = 0; 02139 02140 starttime = (long) time(NULL); 02141 02142 while (*to && !peer) { 02143 int numlines, retry, pos = 1; 02144 struct ast_channel *watchers[AST_MAX_WATCHERS]; 02145 watchers[0] = in; 02146 start = NULL; 02147 02148 for (retry = 0; retry < 2; retry++) { 02149 numlines = 0; 02150 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 02151 if (o->stillgoing) { /* Keep track of important channels */ 02152 stillgoing = 1; 02153 if (o->chan) { 02154 watchers[pos++] = o->chan; 02155 if (!start) 02156 start = o; 02157 else 02158 prev->call_next = o; 02159 prev = o; 02160 } 02161 } 02162 numlines++; 02163 } 02164 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 02165 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 02166 break; 02167 /* On "ringall" strategy we only move to the next penalty level 02168 when *all* ringing phones are done in the current penalty level */ 02169 ring_one(qe, outgoing, &numbusies); 02170 /* and retry... */ 02171 } 02172 if (pos == 1 /* not found */) { 02173 if (numlines == (numbusies + numnochan)) { 02174 ast_log(LOG_DEBUG, "Everyone is busy at this time\n"); 02175 } else { 02176 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan); 02177 } 02178 *to = 0; 02179 return NULL; 02180 } 02181 winner = ast_waitfor_n(watchers, pos, to); 02182 for (o = start; o; o = o->call_next) { 02183 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 02184 if (!peer) { 02185 if (option_verbose > 2) 02186 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02187 peer = o; 02188 } 02189 } else if (o->chan && (o->chan == winner)) { 02190 02191 ast_copy_string(on, o->member->interface, sizeof(on)); 02192 ast_copy_string(membername, o->member->membername, sizeof(membername)); 02193 02194 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 02195 if (option_verbose > 2) 02196 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); 02197 numnochan++; 02198 do_hang(o); 02199 winner = NULL; 02200 continue; 02201 } else if (!ast_strlen_zero(o->chan->call_forward)) { 02202 char tmpchan[256]; 02203 char *stuff; 02204 char *tech; 02205 02206 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 02207 if ((stuff = strchr(tmpchan, '/'))) { 02208 *stuff++ = '\0'; 02209 tech = tmpchan; 02210 } else { 02211 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 02212 stuff = tmpchan; 02213 tech = "Local"; 02214 } 02215 /* Before processing channel, go ahead and check for forwarding */ 02216 if (option_verbose > 2) 02217 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); 02218 /* Setup parameters */ 02219 o->chan = ast_request(tech, in->nativeformats, stuff, &status); 02220 if (!o->chan) { 02221 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); 02222 o->stillgoing = 0; 02223 numnochan++; 02224 } else { 02225 ast_channel_inherit_variables(in, o->chan); 02226 ast_channel_datastore_inherit(in, o->chan); 02227 if (o->chan->cid.cid_num) 02228 free(o->chan->cid.cid_num); 02229 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); 02230 02231 if (o->chan->cid.cid_name) 02232 free(o->chan->cid.cid_name); 02233 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); 02234 02235 ast_string_field_set(o->chan, accountcode, in->accountcode); 02236 o->chan->cdrflags = in->cdrflags; 02237 02238 if (in->cid.cid_ani) { 02239 if (o->chan->cid.cid_ani) 02240 free(o->chan->cid.cid_ani); 02241 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); 02242 } 02243 if (o->chan->cid.cid_rdnis) 02244 free(o->chan->cid.cid_rdnis); 02245 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); 02246 if (ast_call(o->chan, tmpchan, 0)) { 02247 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); 02248 do_hang(o); 02249 numnochan++; 02250 } 02251 } 02252 /* Hangup the original channel now, in case we needed it */ 02253 ast_hangup(winner); 02254 continue; 02255 } 02256 f = ast_read(winner); 02257 if (f) { 02258 if (f->frametype == AST_FRAME_CONTROL) { 02259 switch (f->subclass) { 02260 case AST_CONTROL_ANSWER: 02261 /* This is our guy if someone answered. */ 02262 if (!peer) { 02263 if (option_verbose > 2) 02264 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02265 peer = o; 02266 } 02267 break; 02268 case AST_CONTROL_BUSY: 02269 if (option_verbose > 2) 02270 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); 02271 if (in->cdr) 02272 ast_cdr_busy(in->cdr); 02273 do_hang(o); 02274 endtime = (long)time(NULL); 02275 endtime -= starttime; 02276 rna(endtime * 1000, qe, on, membername, 0); 02277 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02278 if (qe->parent->timeoutrestart) 02279 *to = orig; 02280 ring_one(qe, outgoing, &numbusies); 02281 } 02282 numbusies++; 02283 break; 02284 case AST_CONTROL_CONGESTION: 02285 if (option_verbose > 2) 02286 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name); 02287 if (in->cdr) 02288 ast_cdr_busy(in->cdr); 02289 endtime = (long)time(NULL); 02290 endtime -= starttime; 02291 rna(endtime * 1000, qe, on, membername, 0); 02292 do_hang(o); 02293 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02294 if (qe->parent->timeoutrestart) 02295 *to = orig; 02296 ring_one(qe, outgoing, &numbusies); 02297 } 02298 numbusies++; 02299 break; 02300 case AST_CONTROL_RINGING: 02301 if (option_verbose > 2) 02302 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); 02303 break; 02304 case AST_CONTROL_OFFHOOK: 02305 /* Ignore going off hook */ 02306 break; 02307 default: 02308 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); 02309 } 02310 } 02311 ast_frfree(f); 02312 } else { 02313 endtime = (long) time(NULL) - starttime; 02314 rna(endtime * 1000, qe, on, membername, 1); 02315 do_hang(o); 02316 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02317 if (qe->parent->timeoutrestart) 02318 *to = orig; 02319 ring_one(qe, outgoing, &numbusies); 02320 } 02321 } 02322 } 02323 } 02324 if (winner == in) { 02325 f = ast_read(in); 02326 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02327 /* Got hung up */ 02328 *to = -1; 02329 if (f) 02330 ast_frfree(f); 02331 return NULL; 02332 } 02333 /* First check if DTMF digit is a valid exit */ 02334 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) { 02335 if (option_verbose > 3) 02336 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass); 02337 *to = 0; 02338 *digit = f->subclass; 02339 ast_frfree(f); 02340 return NULL; 02341 } 02342 /* Else check if DTMF should be interpreted as caller disconnect */ 02343 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { 02344 if (option_verbose > 3) 02345 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); 02346 *to = 0; 02347 ast_frfree(f); 02348 return NULL; 02349 } 02350 ast_frfree(f); 02351 } 02352 if (!*to) { 02353 for (o = start; o; o = o->call_next) 02354 rna(orig, qe, o->interface, o->member->membername, 1); 02355 } 02356 } 02357 02358 return peer; 02359 }
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 2454 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, ast_channel::uniqueid, and valid_exit().
Referenced by queue_exec().
02455 { 02456 int res = 0; 02457 02458 /* This is the holding pen for callers 2 through maxlen */ 02459 for (;;) { 02460 enum queue_member_status stat; 02461 02462 if (is_our_turn(qe)) 02463 break; 02464 02465 /* If we have timed out, break out */ 02466 if (qe->expire && (time(NULL) >= qe->expire)) { 02467 *reason = QUEUE_TIMEOUT; 02468 break; 02469 } 02470 02471 stat = get_member_status(qe->parent, qe->max_penalty); 02472 02473 /* leave the queue if no agents, if enabled */ 02474 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 02475 *reason = QUEUE_LEAVEEMPTY; 02476 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02477 leave_queue(qe); 02478 break; 02479 } 02480 02481 /* leave the queue if no reachable agents, if enabled */ 02482 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 02483 *reason = QUEUE_LEAVEUNAVAIL; 02484 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02485 leave_queue(qe); 02486 break; 02487 } 02488 02489 /* Make a position announcement, if enabled */ 02490 if (qe->parent->announcefrequency && !ringing && 02491 (res = say_position(qe))) 02492 break; 02493 02494 /* If we have timed out, break out */ 02495 if (qe->expire && (time(NULL) >= qe->expire)) { 02496 *reason = QUEUE_TIMEOUT; 02497 break; 02498 } 02499 02500 /* Make a periodic announcement, if enabled */ 02501 if (qe->parent->periodicannouncefrequency && !ringing && 02502 (res = say_periodic_announcement(qe))) 02503 break; 02504 02505 /* If we have timed out, break out */ 02506 if (qe->expire && (time(NULL) >= qe->expire)) { 02507 *reason = QUEUE_TIMEOUT; 02508 break; 02509 } 02510 02511 /* Wait a second before checking again */ 02512 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 02513 if (res > 0 && !valid_exit(qe, res)) 02514 res = 0; 02515 else 02516 break; 02517 } 02518 02519 /* If we have timed out, break out */ 02520 if (qe->expire && (time(NULL) >= qe->expire)) { 02521 *reason = QUEUE_TIMEOUT; 02522 break; 02523 } 02524 } 02525 02526 return res; 02527 }
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 = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, .reload = reload, } [static] |
Definition at line 5268 of file app_queue.c.
char* app = "Queue" [static] |
Definition at line 148 of file app_queue.c.
char* app_aqm = "AddQueueMember" [static] |
Definition at line 192 of file app_queue.c.
char* app_aqm_descrip [static] |
Definition at line 194 of file app_queue.c.
char* app_aqm_synopsis = "Dynamically adds queue members" [static] |
Definition at line 193 of file app_queue.c.
char* app_pqm = "PauseQueueMember" [static] |
Definition at line 224 of file app_queue.c.
char* app_pqm_descrip [static] |
Definition at line 226 of file app_queue.c.
char* app_pqm_synopsis = "Pauses a queue member" [static] |
Definition at line 225 of file app_queue.c.
char* app_ql = "QueueLog" [static] |
Definition at line 263 of file app_queue.c.
char* app_ql_descrip [static] |
Initial value:
" QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n" "Allows you to write your own events into the queue log\n" "Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n"
Definition at line 265 of file app_queue.c.
char* app_ql_synopsis = "Writes to the queue_log" [static] |
Definition at line 264 of file app_queue.c.
char* app_rqm = "RemoveQueueMember" [static] |
Definition at line 208 of file app_queue.c.
char* app_rqm_descrip [static] |
Definition at line 210 of file app_queue.c.
char* app_rqm_synopsis = "Dynamically removes queue members" [static] |
Definition at line 209 of file app_queue.c.
char* app_upqm = "UnpauseQueueMember" [static] |
Definition at line 247 of file app_queue.c.
char* app_upqm_descrip [static] |
Definition at line 249 of file app_queue.c.
char* app_upqm_synopsis = "Unpauses a queue member" [static] |
Definition at line 248 of file app_queue.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 5268 of file app_queue.c.
int autofill_default = 0 [static] |
struct ast_cli_entry cli_add_queue_member_deprecated [static] |
Initial value:
{ { "add", "queue", "member", NULL }, handle_queue_add_member, NULL, NULL, complete_queue_add_member }
Definition at line 5157 of file app_queue.c.
struct ast_cli_entry cli_queue[] [static] |
struct ast_cli_entry cli_remove_queue_member_deprecated [static] |
Initial value:
{ { "remove", "queue", "member", NULL }, handle_queue_remove_member, NULL, NULL, complete_queue_remove_member }
Definition at line 5162 of file app_queue.c.
struct ast_cli_entry cli_show_queue_deprecated [static] |
Initial value:
{ { "show", "queue", NULL }, queue_show, NULL, NULL, complete_queue_show }
Definition at line 5152 of file app_queue.c.
Condition for the state change queue
Definition at line 702 of file app_queue.c.
char* descrip [static] |
Definition at line 152 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 704 of file app_queue.c.
enum queue_result id |
struct statechange* last |
Definition at line 704 of file app_queue.c.
Lock for the state change queue
Definition at line 700 of file app_queue.c.
int montype_default = 0 [static] |
const char* pm_family = "Queue/PersistentMembers" [static] |
char qam_cmd_usage[] [static] |
Initial value:
"Usage: queue add member <channel> to <queue> [penalty <penalty>]\n"
Definition at line 5146 of file app_queue.c.
char qrm_cmd_usage[] [static] |
Initial value:
"Usage: queue remove member <channel> from <queue>\n"
Definition at line 5149 of file app_queue.c.
int queue_debug = 0 [static] |
int queue_persistent_members = 0 [static] |
struct { ... } queue_results[] |
Referenced by set_queue_result().
char queue_show_usage[] [static] |
Initial value:
"Usage: queue show\n" " Provides summary information on a specified queue.\n"
Definition at line 5142 of file app_queue.c.
struct ast_datastore_info queue_transfer_info [static] |
Initial value:
{ .type = "queue_transfer", .chan_fixup = queue_transfer_fixup, .destroy = queue_transfer_destroy, }
Definition at line 2623 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] |
struct ast_custom_function queuemembercount_function [static] |
struct ast_custom_function queuememberlist_function [static] |
struct ast_custom_function queuewaitingcount_function [static] |
struct { ... } state_change_q |
Queue of state changes
unsigned int stop |
Set to 1 to stop the thread
Definition at line 696 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 150 of file app_queue.c.
char* text |
Definition at line 302 of file app_queue.c.
Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), handle_response(), method_match(), parse_sip_options(), referstatus2str(), reqprep(), sendtext_exec(), set_queue_result(), sip_alloc(), and sip_show_channel().
pthread_t thread |
The device state monitoring thread
Definition at line 698 of file app_queue.c.
int use_weight = 0 [static] |