#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) |
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 2107 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 4166 of file app_queue.c.
04166 { 04167 QMC_VALID = 0, /* Count valid members */ 04168 QMC_PAUSED, /* Count paused members */ 04169 QMC_ACTIVE, /* Count active members */ 04170 QMC_FREE, /* Count free members */ 04171 QMC_ALL /* Count all queue members */ 04172 };
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 4583 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().
04584 { 04585 struct call_queue *q; 04586 struct queue_ent *qe; 04587 struct member *mem; 04588 int pos, queue_show; 04589 time_t now; 04590 char max_buf[150]; 04591 char *max; 04592 size_t max_left; 04593 float sl = 0; 04594 char *term = manager ? "\r\n" : "\n"; 04595 struct ao2_iterator mem_iter; 04596 04597 time(&now); 04598 if (argc == 2) 04599 queue_show = 0; 04600 else if (argc == 3) 04601 queue_show = 1; 04602 else 04603 return RESULT_SHOWUSAGE; 04604 04605 /* We only want to load realtime queues when a specific queue is asked for. */ 04606 if (queue_show) { 04607 load_realtime_queue(argv[2]); 04608 } else if (ast_check_realtime("queues")) { 04609 struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL); 04610 char *queuename; 04611 if (cfg) { 04612 for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) { 04613 load_realtime_queue(queuename); 04614 } 04615 ast_config_destroy(cfg); 04616 } 04617 } 04618 04619 AST_LIST_LOCK(&queues); 04620 if (AST_LIST_EMPTY(&queues)) { 04621 AST_LIST_UNLOCK(&queues); 04622 if (queue_show) { 04623 if (s) 04624 astman_append(s, "No such queue: %s.%s",argv[2], term); 04625 else 04626 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 04627 } else { 04628 if (s) 04629 astman_append(s, "No queues.%s", term); 04630 else 04631 ast_cli(fd, "No queues.%s", term); 04632 } 04633 return RESULT_SUCCESS; 04634 } 04635 AST_LIST_TRAVERSE(&queues, q, list) { 04636 ast_mutex_lock(&q->lock); 04637 if (queue_show) { 04638 if (strcasecmp(q->name, argv[2]) != 0) { 04639 ast_mutex_unlock(&q->lock); 04640 if (!AST_LIST_NEXT(q, list)) { 04641 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 04642 break; 04643 } 04644 continue; 04645 } 04646 } 04647 max_buf[0] = '\0'; 04648 max = max_buf; 04649 max_left = sizeof(max_buf); 04650 if (q->maxlen) 04651 ast_build_string(&max, &max_left, "%d", q->maxlen); 04652 else 04653 ast_build_string(&max, &max_left, "unlimited"); 04654 sl = 0; 04655 if (q->callscompleted > 0) 04656 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 04657 if (s) 04658 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", 04659 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit, 04660 q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term); 04661 else 04662 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", 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 if (ao2_container_count(q->members)) { 04666 if (s) 04667 astman_append(s, " Members: %s", term); 04668 else 04669 ast_cli(fd, " Members: %s", term); 04670 mem_iter = ao2_iterator_init(q->members, 0); 04671 while ((mem = ao2_iterator_next(&mem_iter))) { 04672 max_buf[0] = '\0'; 04673 max = max_buf; 04674 max_left = sizeof(max_buf); 04675 if (strcasecmp(mem->membername, mem->interface)) { 04676 ast_build_string(&max, &max_left, " (%s)", mem->interface); 04677 } 04678 if (mem->penalty) 04679 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty); 04680 if (mem->dynamic) 04681 ast_build_string(&max, &max_left, " (dynamic)"); 04682 if (mem->realtime) 04683 ast_build_string(&max, &max_left, " (realtime)"); 04684 if (mem->paused) 04685 ast_build_string(&max, &max_left, " (paused)"); 04686 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status)); 04687 if (mem->calls) { 04688 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)", 04689 mem->calls, (long) (time(NULL) - mem->lastcall)); 04690 } else 04691 ast_build_string(&max, &max_left, " has taken no calls yet"); 04692 if (s) 04693 astman_append(s, " %s%s%s", mem->membername, max_buf, term); 04694 else 04695 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term); 04696 ao2_ref(mem, -1); 04697 } 04698 } else if (s) 04699 astman_append(s, " No Members%s", term); 04700 else 04701 ast_cli(fd, " No Members%s", term); 04702 if (q->head) { 04703 pos = 1; 04704 if (s) 04705 astman_append(s, " Callers: %s", term); 04706 else 04707 ast_cli(fd, " Callers: %s", term); 04708 for (qe = q->head; qe; qe = qe->next) { 04709 if (s) 04710 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", 04711 pos++, qe->chan->name, (long) (now - qe->start) / 60, 04712 (long) (now - qe->start) % 60, qe->prio, term); 04713 else 04714 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, 04715 qe->chan->name, (long) (now - qe->start) / 60, 04716 (long) (now - qe->start) % 60, qe->prio, term); 04717 } 04718 } else if (s) 04719 astman_append(s, " No Callers%s", term); 04720 else 04721 ast_cli(fd, " No Callers%s", term); 04722 if (s) 04723 astman_append(s, "%s", term); 04724 else 04725 ast_cli(fd, "%s", term); 04726 ast_mutex_unlock(&q->lock); 04727 if (queue_show) 04728 break; 04729 } 04730 AST_LIST_UNLOCK(&queues); 04731 return RESULT_SUCCESS; 04732 }
static void __reg_module | ( | void | ) | [static] |
Definition at line 5264 of file app_queue.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 5264 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 3401 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().
03402 { 03403 struct call_queue *q; 03404 struct member *new_member, *old_member; 03405 int res = RES_NOSUCHQUEUE; 03406 03407 /* \note Ensure the appropriate realtime queue is loaded. Note that this 03408 * short-circuits if the queue is already in memory. */ 03409 if (!(q = load_realtime_queue(queuename))) 03410 return res; 03411 03412 AST_LIST_LOCK(&queues); 03413 03414 ast_mutex_lock(&q->lock); 03415 if ((old_member = interface_exists(q, interface)) == NULL) { 03416 add_to_interfaces(interface); 03417 if ((new_member = create_queue_member(interface, membername, penalty, paused))) { 03418 new_member->dynamic = 1; 03419 ao2_link(q->members, new_member); 03420 q->membercount++; 03421 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 03422 "Queue: %s\r\n" 03423 "Location: %s\r\n" 03424 "MemberName: %s\r\n" 03425 "Membership: %s\r\n" 03426 "Penalty: %d\r\n" 03427 "CallsTaken: %d\r\n" 03428 "LastCall: %d\r\n" 03429 "Status: %d\r\n" 03430 "Paused: %d\r\n", 03431 q->name, new_member->interface, new_member->membername, 03432 "dynamic", 03433 new_member->penalty, new_member->calls, (int) new_member->lastcall, 03434 new_member->status, new_member->paused); 03435 03436 ao2_ref(new_member, -1); 03437 new_member = NULL; 03438 03439 if (dump) 03440 dump_queue_members(q); 03441 03442 res = RES_OKAY; 03443 } else { 03444 res = RES_OUTOFMEMORY; 03445 } 03446 } else { 03447 ao2_ref(old_member, -1); 03448 res = RES_EXISTS; 03449 } 03450 ast_mutex_unlock(&q->lock); 03451 AST_LIST_UNLOCK(&queues); 03452 03453 return res; 03454 }
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 3780 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().
03781 { 03782 int res=-1; 03783 struct ast_module_user *lu; 03784 char *parse, *temppos = NULL; 03785 int priority_jump = 0; 03786 AST_DECLARE_APP_ARGS(args, 03787 AST_APP_ARG(queuename); 03788 AST_APP_ARG(interface); 03789 AST_APP_ARG(penalty); 03790 AST_APP_ARG(options); 03791 AST_APP_ARG(membername); 03792 ); 03793 int penalty = 0; 03794 03795 if (ast_strlen_zero(data)) { 03796 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n"); 03797 return -1; 03798 } 03799 03800 parse = ast_strdupa(data); 03801 03802 AST_STANDARD_APP_ARGS(args, parse); 03803 03804 lu = ast_module_user_add(chan); 03805 03806 if (ast_strlen_zero(args.interface)) { 03807 args.interface = ast_strdupa(chan->name); 03808 temppos = strrchr(args.interface, '-'); 03809 if (temppos) 03810 *temppos = '\0'; 03811 } 03812 03813 if (!ast_strlen_zero(args.penalty)) { 03814 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) { 03815 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 03816 penalty = 0; 03817 } 03818 } 03819 03820 if (args.options) { 03821 if (strchr(args.options, 'j')) 03822 priority_jump = 1; 03823 } 03824 03825 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) { 03826 case RES_OKAY: 03827 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 03828 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 03829 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 03830 res = 0; 03831 break; 03832 case RES_EXISTS: 03833 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 03834 if (priority_jump || ast_opt_priority_jumping) 03835 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 03836 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 03837 res = 0; 03838 break; 03839 case RES_NOSUCHQUEUE: 03840 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 03841 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 03842 res = 0; 03843 break; 03844 case RES_OUTOFMEMORY: 03845 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 03846 break; 03847 } 03848 03849 ast_module_user_remove(lu); 03850 03851 return res; 03852 }
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 2665 of file app_queue.c.
References ast_channel_datastore_find(), and queue_transfer_info.
Referenced by try_calling().
02666 { 02667 return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1; 02668 }
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 2543 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().
02544 { 02545 if (qe->max_penalty && (mem->penalty > qe->max_penalty)) 02546 return -1; 02547 02548 switch (q->strategy) { 02549 case QUEUE_STRATEGY_RINGALL: 02550 /* Everyone equal, except for penalty */ 02551 tmp->metric = mem->penalty * 1000000; 02552 break; 02553 case QUEUE_STRATEGY_ROUNDROBIN: 02554 if (!pos) { 02555 if (!q->wrapped) { 02556 /* No more channels, start over */ 02557 q->rrpos = 0; 02558 } else { 02559 /* Prioritize next entry */ 02560 q->rrpos++; 02561 } 02562 q->wrapped = 0; 02563 } 02564 /* Fall through */ 02565 case QUEUE_STRATEGY_RRMEMORY: 02566 if (pos < q->rrpos) { 02567 tmp->metric = 1000 + pos; 02568 } else { 02569 if (pos > q->rrpos) 02570 /* Indicate there is another priority */ 02571 q->wrapped = 1; 02572 tmp->metric = pos; 02573 } 02574 tmp->metric += mem->penalty * 1000000; 02575 break; 02576 case QUEUE_STRATEGY_RANDOM: 02577 tmp->metric = ast_random() % 1000; 02578 tmp->metric += mem->penalty * 1000000; 02579 break; 02580 case QUEUE_STRATEGY_FEWESTCALLS: 02581 tmp->metric = mem->calls; 02582 tmp->metric += mem->penalty * 1000000; 02583 break; 02584 case QUEUE_STRATEGY_LEASTRECENT: 02585 if (!mem->lastcall) 02586 tmp->metric = 0; 02587 else 02588 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 02589 tmp->metric += mem->penalty * 1000000; 02590 break; 02591 default: 02592 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 02593 break; 02594 } 02595 if (q->ringlimit && (mem->ringcount >= q->ringlimit)) { 02596 tmp->metric += (mem->ringcount / q->ringlimit) * 10000000; 02597 } 02598 if (option_debug) 02599 ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 02600 tmp->metric, mem->interface, mem->ringcount, q->ringlimit); 02601 return 0; 02602 }
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 1732 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().
01733 { 01734 struct call_queue *q; 01735 struct member *mem; 01736 int found = 0; 01737 01738 /* &qlock and &rq->lock already set by try_calling() 01739 * to solve deadlock */ 01740 AST_LIST_TRAVERSE(&queues, q, list) { 01741 if (q == rq) /* don't check myself, could deadlock */ 01742 continue; 01743 ast_mutex_lock(&q->lock); 01744 if (q->count && q->members) { 01745 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 01746 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 01747 if (q->weight > rq->weight) { 01748 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); 01749 found = 1; 01750 } 01751 ao2_ref(mem, -1); 01752 } 01753 } 01754 ast_mutex_unlock(&q->lock); 01755 if (found) 01756 break; 01757 } 01758 return found; 01759 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4739 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().
04740 { 04741 struct call_queue *q; 04742 char *ret = NULL; 04743 int which = 0; 04744 int wordlen = strlen(word); 04745 04746 AST_LIST_LOCK(&queues); 04747 AST_LIST_TRAVERSE(&queues, q, list) { 04748 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 04749 ret = ast_strdup(q->name); 04750 break; 04751 } 04752 } 04753 AST_LIST_UNLOCK(&queues); 04754 04755 return ret; 04756 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5034 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
05035 { 05036 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 05037 switch (pos) { 05038 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 05039 return NULL; 05040 case 4: /* only one possible match, "to" */ 05041 return state == 0 ? ast_strdup("to") : NULL; 05042 case 5: /* <queue> */ 05043 return complete_queue(line, word, pos, state); 05044 case 6: /* only one possible match, "penalty" */ 05045 return state == 0 ? ast_strdup("penalty") : NULL; 05046 case 7: 05047 if (state < 100) { /* 0-99 */ 05048 char *num; 05049 if ((num = ast_malloc(3))) { 05050 sprintf(num, "%d", state); 05051 } 05052 return num; 05053 } else { 05054 return NULL; 05055 } 05056 case 8: /* only one possible match, "as" */ 05057 return state == 0 ? ast_strdup("as") : NULL; 05058 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 05059 return NULL; 05060 default: 05061 return NULL; 05062 } 05063 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 5100 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.
05101 { 05102 int which = 0; 05103 struct call_queue *q; 05104 struct member *m; 05105 struct ao2_iterator mem_iter; 05106 05107 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 05108 if (pos > 5 || pos < 3) 05109 return NULL; 05110 if (pos == 4) /* only one possible match, 'from' */ 05111 return state == 0 ? ast_strdup("from") : NULL; 05112 05113 if (pos == 5) /* No need to duplicate code */ 05114 return complete_queue(line, word, pos, state); 05115 05116 /* here is the case for 3, <member> */ 05117 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ 05118 AST_LIST_TRAVERSE(&queues, q, list) { 05119 ast_mutex_lock(&q->lock); 05120 mem_iter = ao2_iterator_init(q->members, 0); 05121 while ((m = ao2_iterator_next(&mem_iter))) { 05122 if (++which > state) { 05123 char *tmp; 05124 ast_mutex_unlock(&q->lock); 05125 tmp = ast_strdup(m->interface); 05126 ao2_ref(m, -1); 05127 return tmp; 05128 } 05129 ao2_ref(m, -1); 05130 } 05131 ast_mutex_unlock(&q->lock); 05132 } 05133 } 05134 05135 return NULL; 05136 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4758 of file app_queue.c.
References complete_queue().
04759 { 04760 if (pos == 2) 04761 return complete_queue(line, word, pos, state); 04762 return NULL; 04763 }
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 1762 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry().
01763 { 01764 o->stillgoing = 0; 01765 ast_hangup(o->chan); 01766 o->chan = NULL; 01767 }
static void dump_queue_members | ( | struct call_queue * | pm_queue | ) | [static] |
Definition at line 3308 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().
03309 { 03310 struct member *cur_member; 03311 char value[PM_MAX_LEN]; 03312 int value_len = 0; 03313 int res; 03314 struct ao2_iterator mem_iter; 03315 03316 memset(value, 0, sizeof(value)); 03317 03318 if (!pm_queue) 03319 return; 03320 03321 mem_iter = ao2_iterator_init(pm_queue->members, 0); 03322 while ((cur_member = ao2_iterator_next(&mem_iter))) { 03323 if (!cur_member->dynamic) { 03324 ao2_ref(cur_member, -1); 03325 continue; 03326 } 03327 03328 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s", 03329 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername); 03330 03331 ao2_ref(cur_member, -1); 03332 03333 if (res != strlen(value + value_len)) { 03334 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 03335 break; 03336 } 03337 value_len += res; 03338 } 03339 03340 if (value_len && !cur_member) { 03341 if (ast_db_put(pm_family, pm_queue->name, value)) 03342 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 03343 } else 03344 /* Delete the entry if the queue is empty or there is an error */ 03345 ast_db_del(pm_family, pm_queue->name); 03346 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static] |
find the entry with the best metric, or NULL
Definition at line 1953 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
01954 { 01955 struct callattempt *best = NULL, *cur; 01956 01957 for (cur = outgoing; cur; cur = cur->q_next) { 01958 if (cur->stillgoing && /* Not already done */ 01959 !cur->chan && /* Isn't already going */ 01960 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 01961 best = cur; 01962 } 01963 } 01964 01965 return best; 01966 }
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 4980 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.
04981 { 04982 char *queuename, *interface, *membername = NULL; 04983 int penalty; 04984 04985 if ((argc != 6) && (argc != 8) && (argc != 10)) { 04986 return RESULT_SHOWUSAGE; 04987 } else if (strcmp(argv[4], "to")) { 04988 return RESULT_SHOWUSAGE; 04989 } else if ((argc == 8) && strcmp(argv[6], "penalty")) { 04990 return RESULT_SHOWUSAGE; 04991 } else if ((argc == 10) && strcmp(argv[8], "as")) { 04992 return RESULT_SHOWUSAGE; 04993 } 04994 04995 queuename = argv[5]; 04996 interface = argv[3]; 04997 if (argc >= 8) { 04998 if (sscanf(argv[7], "%d", &penalty) == 1) { 04999 if (penalty < 0) { 05000 ast_cli(fd, "Penalty must be >= 0\n"); 05001 penalty = 0; 05002 } 05003 } else { 05004 ast_cli(fd, "Penalty must be an integer >= 0\n"); 05005 penalty = 0; 05006 } 05007 } else { 05008 penalty = 0; 05009 } 05010 05011 if (argc >= 10) { 05012 membername = argv[9]; 05013 } 05014 05015 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) { 05016 case RES_OKAY: 05017 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 05018 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 05019 return RESULT_SUCCESS; 05020 case RES_EXISTS: 05021 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 05022 return RESULT_FAILURE; 05023 case RES_NOSUCHQUEUE: 05024 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 05025 return RESULT_FAILURE; 05026 case RES_OUTOFMEMORY: 05027 ast_cli(fd, "Out of memory\n"); 05028 return RESULT_FAILURE; 05029 default: 05030 return RESULT_FAILURE; 05031 } 05032 }
static int handle_queue_remove_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 5065 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.
05066 { 05067 char *queuename, *interface; 05068 05069 if (argc != 6) { 05070 return RESULT_SHOWUSAGE; 05071 } else if (strcmp(argv[4], "from")) { 05072 return RESULT_SHOWUSAGE; 05073 } 05074 05075 queuename = argv[5]; 05076 interface = argv[3]; 05077 05078 switch (remove_from_queue(queuename, interface)) { 05079 case RES_OKAY: 05080 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 05081 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 05082 return RESULT_SUCCESS; 05083 case RES_EXISTS: 05084 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 05085 return RESULT_FAILURE; 05086 case RES_NOSUCHQUEUE: 05087 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 05088 return RESULT_FAILURE; 05089 case RES_OUTOFMEMORY: 05090 ast_cli(fd, "Out of memory\n"); 05091 return RESULT_FAILURE; 05092 case RES_NOT_DYNAMIC: 05093 ast_cli(fd, "Member not dynamic\n"); 05094 return RESULT_FAILURE; 05095 default: 05096 return RESULT_FAILURE; 05097 } 05098 }
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 1713 of file app_queue.c.
References ao2_ref(), ast_hangup(), callattempt::chan, free, callattempt::member, and callattempt::q_next.
Referenced by try_calling().
01714 { 01715 struct callattempt *oo; 01716 01717 while (outgoing) { 01718 /* Hangup any existing lines we have open */ 01719 if (outgoing->chan && (outgoing->chan != exception)) 01720 ast_hangup(outgoing->chan); 01721 oo = outgoing; 01722 outgoing = outgoing->q_next; 01723 if (oo->member) 01724 ao2_ref(oo->member, -1); 01725 free(oo); 01726 } 01727 }
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 3284 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().
03285 { 03286 struct member *mem; 03287 struct ao2_iterator mem_iter; 03288 03289 if (!q) 03290 return NULL; 03291 03292 mem_iter = ao2_iterator_init(q->members, 0); 03293 while ((mem = ao2_iterator_next(&mem_iter))) { 03294 if (!strcasecmp(interface, mem->interface)) 03295 return mem; 03296 ao2_ref(mem, -1); 03297 } 03298 03299 return NULL; 03300 }
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 2365 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().
02366 { 02367 struct queue_ent *ch; 02368 struct member *cur; 02369 int avl = 0; 02370 int idx = 0; 02371 int res; 02372 02373 if (!qe->parent->autofill) { 02374 /* Atomically read the parent head -- does not need a lock */ 02375 ch = qe->parent->head; 02376 /* If we are now at the top of the head, break out */ 02377 if (ch == qe) { 02378 if (option_debug) 02379 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02380 res = 1; 02381 } else { 02382 if (option_debug) 02383 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02384 res = 0; 02385 } 02386 02387 } else { 02388 /* This needs a lock. How many members are available to be served? */ 02389 ast_mutex_lock(&qe->parent->lock); 02390 02391 ch = qe->parent->head; 02392 02393 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 02394 if (option_debug) 02395 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"); 02396 avl = 1; 02397 } else { 02398 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0); 02399 while ((cur = ao2_iterator_next(&mem_iter))) { 02400 switch (cur->status) { 02401 case AST_DEVICE_INUSE: 02402 if (!qe->parent->ringinuse) 02403 break; 02404 /* else fall through */ 02405 case AST_DEVICE_NOT_INUSE: 02406 case AST_DEVICE_UNKNOWN: 02407 if (!cur->paused) 02408 avl++; 02409 break; 02410 } 02411 ao2_ref(cur, -1); 02412 } 02413 } 02414 02415 if (option_debug) 02416 ast_log(LOG_DEBUG, "There are %d available members.\n", avl); 02417 02418 while ((idx < avl) && (ch) && (ch != qe)) { 02419 if (!ch->pending) 02420 idx++; 02421 ch = ch->next; 02422 } 02423 02424 /* If the queue entry is within avl [the number of available members] calls from the top ... */ 02425 if (ch && idx < avl) { 02426 if (option_debug) 02427 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02428 res = 1; 02429 } else { 02430 if (option_debug) 02431 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02432 res = 0; 02433 } 02434 02435 ast_mutex_unlock(&qe->parent->lock); 02436 } 02437 02438 return res; 02439 }
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 1669 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().
01670 { 01671 struct call_queue *q; 01672 struct queue_ent *cur, *prev = NULL; 01673 int pos = 0; 01674 01675 if (!(q = qe->parent)) 01676 return; 01677 ast_mutex_lock(&q->lock); 01678 01679 prev = NULL; 01680 for (cur = q->head; cur; cur = cur->next) { 01681 if (cur == qe) { 01682 q->count--; 01683 01684 /* Take us out of the queue */ 01685 manager_event(EVENT_FLAG_CALL, "Leave", 01686 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", 01687 qe->chan->name, q->name, q->count, qe->chan->uniqueid); 01688 if (option_debug) 01689 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 01690 /* Take us out of the queue */ 01691 if (prev) 01692 prev->next = cur->next; 01693 else 01694 q->head = cur->next; 01695 } else { 01696 /* Renumber the people after us in the queue based on a new count */ 01697 cur->pos = ++pos; 01698 prev = cur; 01699 } 01700 } 01701 ast_mutex_unlock(&q->lock); 01702 01703 if (q->dead && !q->count) { 01704 /* It's dead and nobody is in it, so kill it */ 01705 AST_LIST_LOCK(&queues); 01706 AST_LIST_REMOVE(&queues, q, list); 01707 AST_LIST_UNLOCK(&queues); 01708 destroy_queue(q); 01709 } 01710 }
static int load_module | ( | void | ) | [static] |
Definition at line 5219 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().
05220 { 05221 int res; 05222 05223 if (!reload_queues()) 05224 return AST_MODULE_LOAD_DECLINE; 05225 05226 if (queue_persistent_members) 05227 reload_queue_members(); 05228 05229 ast_mutex_init(&device_state.lock); 05230 ast_cond_init(&device_state.cond, NULL); 05231 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL); 05232 05233 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 05234 res = ast_register_application(app, queue_exec, synopsis, descrip); 05235 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip); 05236 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip); 05237 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip); 05238 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip); 05239 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip); 05240 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); 05241 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); 05242 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); 05243 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); 05244 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); 05245 res |= ast_custom_function_register(&queueagentcount_function); 05246 res |= ast_custom_function_register(&queuemembercount_function); 05247 res |= ast_custom_function_register(&queuememberlist_function); 05248 res |= ast_custom_function_register(&queuewaitingcount_function); 05249 res |= ast_devstate_add(statechange_queue, NULL); 05250 05251 return res; 05252 }
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 4873 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().
04874 { 04875 const char *queuename, *interface, *penalty_s, *paused_s, *membername; 04876 int paused, penalty = 0; 04877 04878 queuename = astman_get_header(m, "Queue"); 04879 interface = astman_get_header(m, "Interface"); 04880 penalty_s = astman_get_header(m, "Penalty"); 04881 paused_s = astman_get_header(m, "Paused"); 04882 membername = astman_get_header(m, "MemberName"); 04883 04884 if (ast_strlen_zero(queuename)) { 04885 astman_send_error(s, m, "'Queue' not specified."); 04886 return 0; 04887 } 04888 04889 if (ast_strlen_zero(interface)) { 04890 astman_send_error(s, m, "'Interface' not specified."); 04891 return 0; 04892 } 04893 04894 if (ast_strlen_zero(penalty_s)) 04895 penalty = 0; 04896 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0) 04897 penalty = 0; 04898 04899 if (ast_strlen_zero(paused_s)) 04900 paused = 0; 04901 else 04902 paused = abs(ast_true(paused_s)); 04903 04904 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) { 04905 case RES_OKAY: 04906 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 04907 astman_send_ack(s, m, "Added interface to queue"); 04908 break; 04909 case RES_EXISTS: 04910 astman_send_error(s, m, "Unable to add interface: Already there"); 04911 break; 04912 case RES_NOSUCHQUEUE: 04913 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 04914 break; 04915 case RES_OUTOFMEMORY: 04916 astman_send_error(s, m, "Out of memory"); 04917 break; 04918 } 04919 04920 return 0; 04921 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4957 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().
04958 { 04959 const char *queuename, *interface, *paused_s; 04960 int paused; 04961 04962 interface = astman_get_header(m, "Interface"); 04963 paused_s = astman_get_header(m, "Paused"); 04964 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 04965 04966 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 04967 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 04968 return 0; 04969 } 04970 04971 paused = abs(ast_true(paused_s)); 04972 04973 if (set_member_paused(queuename, interface, paused)) 04974 astman_send_error(s, m, "Interface not found"); 04975 else 04976 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 04977 return 0; 04978 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4768 of file app_queue.c.
References __queues_show(), astman_append(), RESULT_SUCCESS, and s.
Referenced by load_module().
04769 { 04770 char *a[] = { "queue", "show" }; 04771 04772 __queues_show(s, 1, -1, 2, a); 04773 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 04774 04775 return RESULT_SUCCESS; 04776 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4779 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().
04780 { 04781 time_t now; 04782 int pos; 04783 const char *id = astman_get_header(m,"ActionID"); 04784 const char *queuefilter = astman_get_header(m,"Queue"); 04785 const char *memberfilter = astman_get_header(m,"Member"); 04786 char idText[256] = ""; 04787 struct call_queue *q; 04788 struct queue_ent *qe; 04789 float sl = 0; 04790 struct member *mem; 04791 struct ao2_iterator mem_iter; 04792 04793 astman_send_ack(s, m, "Queue status will follow"); 04794 time(&now); 04795 AST_LIST_LOCK(&queues); 04796 if (!ast_strlen_zero(id)) 04797 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 04798 04799 AST_LIST_TRAVERSE(&queues, q, list) { 04800 ast_mutex_lock(&q->lock); 04801 04802 /* List queue properties */ 04803 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 04804 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 04805 astman_append(s, "Event: QueueParams\r\n" 04806 "Queue: %s\r\n" 04807 "Max: %d\r\n" 04808 "Calls: %d\r\n" 04809 "Holdtime: %d\r\n" 04810 "Completed: %d\r\n" 04811 "Abandoned: %d\r\n" 04812 "ServiceLevel: %d\r\n" 04813 "ServicelevelPerf: %2.1f\r\n" 04814 "RingLimit: %d\r\n" 04815 "Weight: %d\r\n" 04816 "%s" 04817 "\r\n", 04818 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, 04819 q->callsabandoned, q->servicelevel, sl, q->ringlimit, q->weight, idText); 04820 /* List Queue Members */ 04821 mem_iter = ao2_iterator_init(q->members, 0); 04822 while ((mem = ao2_iterator_next(&mem_iter))) { 04823 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) { 04824 astman_append(s, "Event: QueueMember\r\n" 04825 "Queue: %s\r\n" 04826 "Name: %s\r\n" 04827 "Location: %s\r\n" 04828 "Membership: %s\r\n" 04829 "Penalty: %d\r\n" 04830 "CallsTaken: %d\r\n" 04831 "LastCall: %d\r\n" 04832 "Status: %d\r\n" 04833 "Paused: %d\r\n" 04834 "%s" 04835 "\r\n", 04836 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 04837 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 04838 } 04839 ao2_ref(mem, -1); 04840 } 04841 /* List Queue Entries */ 04842 pos = 1; 04843 for (qe = q->head; qe; qe = qe->next) { 04844 astman_append(s, "Event: QueueEntry\r\n" 04845 "Queue: %s\r\n" 04846 "Position: %d\r\n" 04847 "Channel: %s\r\n" 04848 "CallerID: %s\r\n" 04849 "CallerIDName: %s\r\n" 04850 "Wait: %ld\r\n" 04851 "%s" 04852 "\r\n", 04853 q->name, pos++, qe->chan->name, 04854 S_OR(qe->chan->cid.cid_num, "unknown"), 04855 S_OR(qe->chan->cid.cid_name, "unknown"), 04856 (long) (now - qe->start), idText); 04857 } 04858 } 04859 ast_mutex_unlock(&q->lock); 04860 } 04861 04862 astman_append(s, 04863 "Event: QueueStatusComplete\r\n" 04864 "%s" 04865 "\r\n",idText); 04866 04867 AST_LIST_UNLOCK(&queues); 04868 04869 04870 return RESULT_SUCCESS; 04871 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4923 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().
04924 { 04925 const char *queuename, *interface; 04926 04927 queuename = astman_get_header(m, "Queue"); 04928 interface = astman_get_header(m, "Interface"); 04929 04930 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 04931 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 04932 return 0; 04933 } 04934 04935 switch (remove_from_queue(queuename, interface)) { 04936 case RES_OKAY: 04937 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 04938 astman_send_ack(s, m, "Removed interface from queue"); 04939 break; 04940 case RES_EXISTS: 04941 astman_send_error(s, m, "Unable to remove interface: Not there"); 04942 break; 04943 case RES_NOSUCHQUEUE: 04944 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 04945 break; 04946 case RES_OUTOFMEMORY: 04947 astman_send_error(s, m, "Out of memory"); 04948 break; 04949 case RES_NOT_DYNAMIC: 04950 astman_send_error(s, m, "Member not dynamic"); 04951 break; 04952 } 04953 04954 return 0; 04955 }
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_waitstream(), queue_ent::chan, and ast_channel::language.
Referenced by say_periodic_announcement(), say_position(), and try_calling().
01499 { 01500 int res; 01501 01502 ast_stopstream(chan); 01503 01504 res = ast_streamfile(chan, filename, chan->language); 01505 if (!res) 01506 res = ast_waitstream(chan, AST_DIGIT_ANY); 01507 01508 ast_stopstream(chan); 01509 01510 return res; 01511 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3597 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().
03598 { 03599 struct ast_module_user *lu; 03600 char *parse; 03601 int priority_jump = 0; 03602 int ignore_fail = 0; 03603 AST_DECLARE_APP_ARGS(args, 03604 AST_APP_ARG(queuename); 03605 AST_APP_ARG(interface); 03606 AST_APP_ARG(options); 03607 ); 03608 03609 if (ast_strlen_zero(data)) { 03610 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03611 return -1; 03612 } 03613 03614 parse = ast_strdupa(data); 03615 03616 AST_STANDARD_APP_ARGS(args, parse); 03617 03618 lu = ast_module_user_add(chan); 03619 03620 if (args.options) { 03621 if (strchr(args.options, 'j')) 03622 priority_jump = 1; 03623 if (strchr(args.options, 'i')) 03624 ignore_fail = 1; 03625 } 03626 03627 if (ast_strlen_zero(args.interface)) { 03628 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03629 ast_module_user_remove(lu); 03630 return -1; 03631 } 03632 03633 if (set_member_paused(args.queuename, args.interface, 1)) { 03634 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 03635 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 03636 if (priority_jump || ast_opt_priority_jumping) { 03637 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03638 ast_module_user_remove(lu); 03639 return 0; 03640 } 03641 } 03642 ast_module_user_remove(lu); 03643 if (ignore_fail) { 03644 return 0; 03645 } else { 03646 return -1; 03647 } 03648 } 03649 03650 ast_module_user_remove(lu); 03651 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 03652 return 0; 03653 }
static int ql_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3854 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().
03855 { 03856 struct ast_module_user *u; 03857 char *parse; 03858 03859 AST_DECLARE_APP_ARGS(args, 03860 AST_APP_ARG(queuename); 03861 AST_APP_ARG(uniqueid); 03862 AST_APP_ARG(membername); 03863 AST_APP_ARG(event); 03864 AST_APP_ARG(params); 03865 ); 03866 03867 if (ast_strlen_zero(data)) { 03868 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n"); 03869 return -1; 03870 } 03871 03872 u = ast_module_user_add(chan); 03873 03874 parse = ast_strdupa(data); 03875 03876 AST_STANDARD_APP_ARGS(args, parse); 03877 03878 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 03879 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 03880 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n"); 03881 ast_module_user_remove(u); 03882 return -1; 03883 } 03884 03885 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 03886 "%s", args.params ? args.params : ""); 03887 03888 ast_module_user_remove(u); 03889 03890 return 0; 03891 }
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 3905 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().
03906 { 03907 int res=-1; 03908 int ringing=0; 03909 struct ast_module_user *lu; 03910 const char *user_priority; 03911 const char *max_penalty_str; 03912 int prio; 03913 int max_penalty; 03914 enum queue_result reason = QUEUE_UNKNOWN; 03915 /* whether to exit Queue application after the timeout hits */ 03916 int tries = 0; 03917 int noption = 0; 03918 char *parse; 03919 AST_DECLARE_APP_ARGS(args, 03920 AST_APP_ARG(queuename); 03921 AST_APP_ARG(options); 03922 AST_APP_ARG(url); 03923 AST_APP_ARG(announceoverride); 03924 AST_APP_ARG(queuetimeoutstr); 03925 AST_APP_ARG(agi); 03926 ); 03927 /* Our queue entry */ 03928 struct queue_ent qe; 03929 03930 if (ast_strlen_zero(data)) { 03931 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n"); 03932 return -1; 03933 } 03934 03935 parse = ast_strdupa(data); 03936 AST_STANDARD_APP_ARGS(args, parse); 03937 03938 lu = ast_module_user_add(chan); 03939 03940 /* Setup our queue entry */ 03941 memset(&qe, 0, sizeof(qe)); 03942 qe.start = time(NULL); 03943 03944 /* set the expire time based on the supplied timeout; */ 03945 if (!ast_strlen_zero(args.queuetimeoutstr)) 03946 qe.expire = qe.start + atoi(args.queuetimeoutstr); 03947 else 03948 qe.expire = 0; 03949 03950 /* Get the priority from the variable ${QUEUE_PRIO} */ 03951 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 03952 if (user_priority) { 03953 if (sscanf(user_priority, "%d", &prio) == 1) { 03954 if (option_debug) 03955 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", 03956 chan->name, prio); 03957 } else { 03958 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 03959 user_priority, chan->name); 03960 prio = 0; 03961 } 03962 } else { 03963 if (option_debug > 2) 03964 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); 03965 prio = 0; 03966 } 03967 03968 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 03969 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 03970 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) { 03971 if (option_debug) 03972 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", 03973 chan->name, max_penalty); 03974 } else { 03975 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 03976 max_penalty_str, chan->name); 03977 max_penalty = 0; 03978 } 03979 } else { 03980 max_penalty = 0; 03981 } 03982 03983 if (args.options && (strchr(args.options, 'r'))) 03984 ringing = 1; 03985 03986 if (option_debug) 03987 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 03988 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 03989 03990 qe.chan = chan; 03991 qe.prio = prio; 03992 qe.max_penalty = max_penalty; 03993 qe.last_pos_said = 0; 03994 qe.last_pos = 0; 03995 qe.last_periodic_announce_time = time(NULL); 03996 qe.last_periodic_announce_sound = 0; 03997 qe.valid_digits = 0; 03998 if (!join_queue(args.queuename, &qe, &reason)) { 03999 int makeannouncement = 0; 04000 04001 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), 04002 S_OR(chan->cid.cid_num, "")); 04003 check_turns: 04004 if (ringing) { 04005 ast_indicate(chan, AST_CONTROL_RINGING); 04006 } else { 04007 ast_moh_start(chan, qe.moh, NULL); 04008 } 04009 04010 /* This is the wait loop for callers 2 through maxlen */ 04011 res = wait_our_turn(&qe, ringing, &reason); 04012 if (res) 04013 goto stop; 04014 04015 for (;;) { 04016 /* This is the wait loop for the head caller*/ 04017 /* To exit, they may get their call answered; */ 04018 /* they may dial a digit from the queue context; */ 04019 /* or, they may timeout. */ 04020 04021 enum queue_member_status stat; 04022 04023 /* Leave if we have exceeded our queuetimeout */ 04024 if (qe.expire && (time(NULL) >= qe.expire)) { 04025 record_abandoned(&qe); 04026 reason = QUEUE_TIMEOUT; 04027 res = 0; 04028 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04029 break; 04030 } 04031 04032 if (makeannouncement) { 04033 /* Make a position announcement, if enabled */ 04034 if (qe.parent->announcefrequency && !ringing) 04035 if ((res = say_position(&qe))) 04036 goto stop; 04037 04038 } 04039 makeannouncement = 1; 04040 04041 /* Leave if we have exceeded our queuetimeout */ 04042 if (qe.expire && (time(NULL) >= qe.expire)) { 04043 record_abandoned(&qe); 04044 reason = QUEUE_TIMEOUT; 04045 res = 0; 04046 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04047 break; 04048 } 04049 /* Make a periodic announcement, if enabled */ 04050 if (qe.parent->periodicannouncefrequency && !ringing) 04051 if ((res = say_periodic_announcement(&qe))) 04052 goto stop; 04053 04054 /* Leave if we have exceeded our queuetimeout */ 04055 if (qe.expire && (time(NULL) >= qe.expire)) { 04056 record_abandoned(&qe); 04057 reason = QUEUE_TIMEOUT; 04058 res = 0; 04059 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04060 break; 04061 } 04062 /* Try calling all queue members for 'timeout' seconds */ 04063 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi); 04064 if (res) 04065 goto stop; 04066 04067 stat = get_member_status(qe.parent, qe.max_penalty); 04068 04069 /* exit after 'timeout' cycle if 'n' option enabled */ 04070 if (noption && tries >= qe.parent->membercount) { 04071 if (option_verbose > 2) 04072 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n"); 04073 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04074 record_abandoned(&qe); 04075 reason = QUEUE_TIMEOUT; 04076 res = 0; 04077 break; 04078 } 04079 04080 /* leave the queue if no agents, if enabled */ 04081 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 04082 record_abandoned(&qe); 04083 reason = QUEUE_LEAVEEMPTY; 04084 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04085 res = 0; 04086 break; 04087 } 04088 04089 /* leave the queue if no reachable agents, if enabled */ 04090 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 04091 record_abandoned(&qe); 04092 reason = QUEUE_LEAVEUNAVAIL; 04093 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 04094 res = 0; 04095 break; 04096 } 04097 04098 /* Leave if we have exceeded our queuetimeout */ 04099 if (qe.expire && (time(NULL) >= qe.expire)) { 04100 record_abandoned(&qe); 04101 reason = QUEUE_TIMEOUT; 04102 res = 0; 04103 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 04104 break; 04105 } 04106 04107 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 04108 update_realtime_members(qe.parent); 04109 04110 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 04111 res = wait_a_bit(&qe); 04112 if (res) 04113 goto stop; 04114 04115 /* Since this is a priority queue and 04116 * it is not sure that we are still at the head 04117 * of the queue, go and check for our turn again. 04118 */ 04119 if (!is_our_turn(&qe)) { 04120 if (option_debug) 04121 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n", 04122 qe.chan->name); 04123 goto check_turns; 04124 } 04125 } 04126 04127 stop: 04128 if (res) { 04129 if (res < 0) { 04130 if (!qe.handled) { 04131 record_abandoned(&qe); 04132 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 04133 "%d|%d|%ld", qe.pos, qe.opos, 04134 (long) time(NULL) - qe.start); 04135 } 04136 res = -1; 04137 } else if (qe.valid_digits) { 04138 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 04139 "%s|%d", qe.digits, qe.pos); 04140 } 04141 } 04142 04143 /* Don't allow return code > 0 */ 04144 if (res >= 0) { 04145 res = 0; 04146 if (ringing) { 04147 ast_indicate(chan, -1); 04148 } else { 04149 ast_moh_stop(chan); 04150 } 04151 ast_stopstream(chan); 04152 } 04153 leave_queue(&qe); 04154 if (reason != QUEUE_UNKNOWN) 04155 set_queue_result(chan, reason); 04156 } else { 04157 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 04158 set_queue_result(chan, reason); 04159 res = 0; 04160 } 04161 ast_module_user_remove(lu); 04162 04163 return res; 04164 }
static int queue_function_queuemembercount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4174 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.
04175 { 04176 int count = 0; 04177 struct call_queue *q; 04178 struct ast_module_user *lu; 04179 struct member *m; 04180 struct ao2_iterator mem_iter; 04181 char *name, *item; 04182 enum qmc_status mode = QMC_VALID; 04183 04184 buf[0] = '\0'; 04185 04186 if (ast_strlen_zero(data)) { 04187 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04188 return -1; 04189 } 04190 04191 name = ast_strdupa(data); 04192 04193 lu = ast_module_user_add(chan); 04194 04195 if ((item = strchr(name, ':'))) { 04196 *item = '\0'; 04197 item++; 04198 } else { 04199 item = ""; 04200 } 04201 04202 if (!strcasecmp(item, "valid")) { 04203 mode = QMC_VALID; 04204 } else if (!strcasecmp(item, "paused")) { 04205 mode = QMC_PAUSED; 04206 } else if (!strcasecmp(item, "active")) { 04207 mode = QMC_ACTIVE; 04208 } else if (!strcasecmp(item, "free")) { 04209 mode = QMC_FREE; 04210 } else if (!strcasecmp(item, "all")) { 04211 mode = QMC_ALL; 04212 } 04213 04214 if ((q = load_realtime_queue(name))) { 04215 ast_mutex_lock(&q->lock); 04216 mem_iter = ao2_iterator_init(q->members, 0); 04217 while ((m = ao2_iterator_next(&mem_iter))) { 04218 switch (mode) { 04219 case QMC_VALID: 04220 /* Count the queue members who are logged in and presently answering calls */ 04221 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 04222 count++; 04223 } 04224 break; 04225 case QMC_PAUSED: 04226 /* Count paused members */ 04227 if (m->paused) { 04228 count++; 04229 } 04230 break; 04231 case QMC_ACTIVE: 04232 /* Count not paused members who are logged in and presently answering calls */ 04233 if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 04234 count++; 04235 } 04236 break; 04237 case QMC_FREE: 04238 /* Count free members in the queue */ 04239 if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) { 04240 count++; 04241 } 04242 break; 04243 default: 04244 count++; 04245 break; 04246 } 04247 ao2_ref(m, -1); 04248 } 04249 ast_mutex_unlock(&q->lock); 04250 } else 04251 ast_log(LOG_WARNING, "queue %s was not found\n", name); 04252 04253 snprintf(buf, len, "%d", count); 04254 ast_module_user_remove(lu); 04255 04256 return 0; 04257 }
static int queue_function_queuememberlist | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4302 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.
04303 { 04304 struct ast_module_user *u; 04305 struct call_queue *q; 04306 struct member *m; 04307 04308 /* Ensure an otherwise empty list doesn't return garbage */ 04309 buf[0] = '\0'; 04310 04311 if (ast_strlen_zero(data)) { 04312 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 04313 return -1; 04314 } 04315 04316 u = ast_module_user_add(chan); 04317 04318 AST_LIST_LOCK(&queues); 04319 AST_LIST_TRAVERSE(&queues, q, list) { 04320 if (!strcasecmp(q->name, data)) { 04321 ast_mutex_lock(&q->lock); 04322 break; 04323 } 04324 } 04325 AST_LIST_UNLOCK(&queues); 04326 04327 if (q) { 04328 int buflen = 0, count = 0; 04329 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 04330 04331 while ((m = ao2_iterator_next(&mem_iter))) { 04332 /* strcat() is always faster than printf() */ 04333 if (count++) { 04334 strncat(buf + buflen, ",", len - buflen - 1); 04335 buflen++; 04336 } 04337 strncat(buf + buflen, m->membername, len - buflen - 1); 04338 buflen += strlen(m->membername); 04339 /* Safeguard against overflow (negative length) */ 04340 if (buflen >= len - 2) { 04341 ao2_ref(m, -1); 04342 ast_log(LOG_WARNING, "Truncating list\n"); 04343 break; 04344 } 04345 ao2_ref(m, -1); 04346 } 04347 ast_mutex_unlock(&q->lock); 04348 } else 04349 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04350 04351 /* We should already be terminated, but let's make sure. */ 04352 buf[len - 1] = '\0'; 04353 ast_module_user_remove(u); 04354 04355 return 0; 04356 }
static int queue_function_queuewaitingcount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4259 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.
04260 { 04261 int count = 0; 04262 struct call_queue *q; 04263 struct ast_module_user *lu; 04264 struct ast_variable *var = NULL; 04265 04266 buf[0] = '\0'; 04267 04268 if (ast_strlen_zero(data)) { 04269 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 04270 return -1; 04271 } 04272 04273 lu = ast_module_user_add(chan); 04274 04275 AST_LIST_LOCK(&queues); 04276 AST_LIST_TRAVERSE(&queues, q, list) { 04277 if (!strcasecmp(q->name, data)) { 04278 ast_mutex_lock(&q->lock); 04279 break; 04280 } 04281 } 04282 AST_LIST_UNLOCK(&queues); 04283 04284 if (q) { 04285 count = q->count; 04286 ast_mutex_unlock(&q->lock); 04287 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) { 04288 /* if the queue is realtime but was not found in memory, this 04289 * means that the queue had been deleted from memory since it was 04290 * "dead." This means it has a 0 waiting count 04291 */ 04292 count = 0; 04293 ast_variables_destroy(var); 04294 } else 04295 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04296 04297 snprintf(buf, len, "%d", count); 04298 ast_module_user_remove(lu); 04299 return 0; 04300 }
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 4734 of file app_queue.c.
References __queues_show().
Referenced by __queues_show().
04735 { 04736 return __queues_show(NULL, 0, fd, argc, argv); 04737 }
static void queue_transfer_destroy | ( | void * | data | ) | [static] |
Definition at line 2611 of file app_queue.c.
References ast_free.
02612 { 02613 struct queue_transfer_ds *qtds = data; 02614 ast_free(qtds); 02615 }
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 2634 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().
02635 { 02636 struct queue_transfer_ds *qtds = data; 02637 struct queue_ent *qe = qtds->qe; 02638 struct member *member = qtds->member; 02639 time_t callstart = qtds->starttime; 02640 int callcompletedinsl = qtds->callcompletedinsl; 02641 struct ast_datastore *datastore; 02642 02643 ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 02644 new_chan->exten, new_chan->context, (long) (callstart - qe->start), 02645 (long) (time(NULL) - callstart)); 02646 02647 update_queue(qe->parent, member, callcompletedinsl); 02648 02649 /* No need to lock the channels because they are already locked in ast_do_masquerade */ 02650 if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) { 02651 ast_channel_datastore_remove(old_chan, datastore); 02652 } else { 02653 ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n"); 02654 } 02655 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 1654 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().
01655 { 01656 int oldvalue; 01657 01658 /* Calculate holdtime using an exponential average */ 01659 /* Thanks to SRT for this contribution */ 01660 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 01661 01662 ast_mutex_lock(&qe->parent->lock); 01663 oldvalue = qe->parent->holdtime; 01664 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 01665 ast_mutex_unlock(&qe->parent->lock); 01666 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 2074 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().
02075 { 02076 ast_mutex_lock(&qe->parent->lock); 02077 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 02078 "Queue: %s\r\n" 02079 "Uniqueid: %s\r\n" 02080 "Position: %d\r\n" 02081 "OriginalPosition: %d\r\n" 02082 "HoldTime: %d\r\n", 02083 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 02084 02085 qe->parent->callsabandoned++; 02086 ast_mutex_unlock(&qe->parent->lock); 02087 }
static int reload | ( | void | ) | [static] |
Definition at line 5254 of file app_queue.c.
References reload_queues().
05255 { 05256 reload_queues(); 05257 return 0; 05258 }
static void reload_queue_members | ( | void | ) | [static] |
Definition at line 3502 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().
03503 { 03504 char *cur_ptr; 03505 char *queue_name; 03506 char *member; 03507 char *interface; 03508 char *membername = NULL; 03509 char *penalty_tok; 03510 int penalty = 0; 03511 char *paused_tok; 03512 int paused = 0; 03513 struct ast_db_entry *db_tree; 03514 struct ast_db_entry *entry; 03515 struct call_queue *cur_queue; 03516 char queue_data[PM_MAX_LEN]; 03517 03518 AST_LIST_LOCK(&queues); 03519 03520 /* Each key in 'pm_family' is the name of a queue */ 03521 db_tree = ast_db_gettree(pm_family, NULL); 03522 for (entry = db_tree; entry; entry = entry->next) { 03523 03524 queue_name = entry->key + strlen(pm_family) + 2; 03525 03526 AST_LIST_TRAVERSE(&queues, cur_queue, list) { 03527 ast_mutex_lock(&cur_queue->lock); 03528 if (!strcmp(queue_name, cur_queue->name)) 03529 break; 03530 ast_mutex_unlock(&cur_queue->lock); 03531 } 03532 03533 if (!cur_queue) 03534 cur_queue = load_realtime_queue(queue_name); 03535 03536 if (!cur_queue) { 03537 /* If the queue no longer exists, remove it from the 03538 * database */ 03539 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 03540 ast_db_del(pm_family, queue_name); 03541 continue; 03542 } else 03543 ast_mutex_unlock(&cur_queue->lock); 03544 03545 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) 03546 continue; 03547 03548 cur_ptr = queue_data; 03549 while ((member = strsep(&cur_ptr, "|"))) { 03550 if (ast_strlen_zero(member)) 03551 continue; 03552 03553 interface = strsep(&member, ";"); 03554 penalty_tok = strsep(&member, ";"); 03555 paused_tok = strsep(&member, ";"); 03556 membername = strsep(&member, ";"); 03557 03558 if (!penalty_tok) { 03559 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 03560 break; 03561 } 03562 penalty = strtol(penalty_tok, NULL, 10); 03563 if (errno == ERANGE) { 03564 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 03565 break; 03566 } 03567 03568 if (!paused_tok) { 03569 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 03570 break; 03571 } 03572 paused = strtol(paused_tok, NULL, 10); 03573 if ((errno == ERANGE) || paused < 0 || paused > 1) { 03574 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 03575 break; 03576 } 03577 if (ast_strlen_zero(membername)) 03578 membername = interface; 03579 03580 if (option_debug) 03581 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 03582 03583 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) { 03584 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 03585 break; 03586 } 03587 } 03588 } 03589 03590 AST_LIST_UNLOCK(&queues); 03591 if (db_tree) { 03592 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 03593 ast_db_freetree(db_tree); 03594 } 03595 }
static int reload_queues | ( | void | ) | [static] |
Definition at line 4401 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().
04402 { 04403 struct call_queue *q; 04404 struct ast_config *cfg; 04405 char *cat, *tmp; 04406 struct ast_variable *var; 04407 struct member *cur, *newm; 04408 struct ao2_iterator mem_iter; 04409 int new; 04410 const char *general_val = NULL; 04411 char parse[80]; 04412 char *interface; 04413 char *membername = NULL; 04414 int penalty; 04415 AST_DECLARE_APP_ARGS(args, 04416 AST_APP_ARG(interface); 04417 AST_APP_ARG(penalty); 04418 AST_APP_ARG(membername); 04419 ); 04420 04421 if (!(cfg = ast_config_load("queues.conf"))) { 04422 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 04423 return 0; 04424 } 04425 AST_LIST_LOCK(&queues); 04426 use_weight=0; 04427 /* Mark all non-realtime queues as dead for the moment */ 04428 AST_LIST_TRAVERSE(&queues, q, list) { 04429 if (!q->realtime) { 04430 q->dead = 1; 04431 q->found = 0; 04432 } 04433 } 04434 04435 /* Chug through config file */ 04436 cat = NULL; 04437 while ((cat = ast_category_browse(cfg, cat)) ) { 04438 if (!strcasecmp(cat, "general")) { 04439 /* Initialize global settings */ 04440 queue_debug = 0; 04441 if ((general_val = ast_variable_retrieve(cfg, "general", "debug"))) 04442 queue_debug = ast_true(general_val); 04443 queue_persistent_members = 0; 04444 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 04445 queue_persistent_members = ast_true(general_val); 04446 autofill_default = 0; 04447 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 04448 autofill_default = ast_true(general_val); 04449 montype_default = 0; 04450 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) 04451 if (!strcasecmp(general_val, "mixmonitor")) 04452 montype_default = 1; 04453 } else { /* Define queue */ 04454 /* Look for an existing one */ 04455 AST_LIST_TRAVERSE(&queues, q, list) { 04456 if (!strcmp(q->name, cat)) 04457 break; 04458 } 04459 if (!q) { 04460 /* Make one then */ 04461 if (!(q = alloc_queue(cat))) { 04462 /* TODO: Handle memory allocation failure */ 04463 } 04464 new = 1; 04465 } else 04466 new = 0; 04467 if (q) { 04468 if (!new) 04469 ast_mutex_lock(&q->lock); 04470 /* Check if a queue with this name already exists */ 04471 if (q->found) { 04472 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat); 04473 if (!new) 04474 ast_mutex_unlock(&q->lock); 04475 continue; 04476 } 04477 /* Re-initialize the queue, and clear statistics */ 04478 init_queue(q); 04479 clear_queue(q); 04480 mem_iter = ao2_iterator_init(q->members, 0); 04481 while ((cur = ao2_iterator_next(&mem_iter))) { 04482 if (!cur->dynamic) { 04483 cur->delme = 1; 04484 } 04485 ao2_ref(cur, -1); 04486 } 04487 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 04488 if (!strcasecmp(var->name, "member")) { 04489 struct member tmpmem; 04490 membername = NULL; 04491 04492 /* Add a new member */ 04493 ast_copy_string(parse, var->value, sizeof(parse)); 04494 04495 AST_NONSTANDARD_APP_ARGS(args, parse, ','); 04496 04497 interface = args.interface; 04498 if (!ast_strlen_zero(args.penalty)) { 04499 tmp = args.penalty; 04500 while (*tmp && *tmp < 33) tmp++; 04501 penalty = atoi(tmp); 04502 if (penalty < 0) { 04503 penalty = 0; 04504 } 04505 } else 04506 penalty = 0; 04507 04508 if (!ast_strlen_zero(args.membername)) { 04509 membername = args.membername; 04510 while (*membername && *membername < 33) membername++; 04511 } 04512 04513 /* Find the old position in the list */ 04514 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04515 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 04516 04517 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); 04518 ao2_link(q->members, newm); 04519 ao2_ref(newm, -1); 04520 newm = NULL; 04521 04522 if (cur) 04523 ao2_ref(cur, -1); 04524 else { 04525 /* Add them to the master int list if necessary */ 04526 add_to_interfaces(interface); 04527 q->membercount++; 04528 } 04529 } else { 04530 queue_set_param(q, var->name, var->value, var->lineno, 1); 04531 } 04532 } 04533 04534 /* Free remaining members marked as delme */ 04535 mem_iter = ao2_iterator_init(q->members, 0); 04536 while ((cur = ao2_iterator_next(&mem_iter))) { 04537 if (! cur->delme) { 04538 ao2_ref(cur, -1); 04539 continue; 04540 } 04541 04542 q->membercount--; 04543 ao2_unlink(q->members, cur); 04544 remove_from_interfaces(cur->interface); 04545 ao2_ref(cur, -1); 04546 } 04547 04548 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 04549 rr_dep_warning(); 04550 04551 if (new) { 04552 AST_LIST_INSERT_HEAD(&queues, q, list); 04553 } else 04554 ast_mutex_unlock(&q->lock); 04555 } 04556 } 04557 } 04558 ast_config_destroy(cfg); 04559 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) { 04560 if (q->dead) { 04561 AST_LIST_REMOVE_CURRENT(&queues, list); 04562 if (!q->count) 04563 destroy_queue(q); 04564 else 04565 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n"); 04566 } else { 04567 ast_mutex_lock(&q->lock); 04568 mem_iter = ao2_iterator_init(q->members, 0); 04569 while ((cur = ao2_iterator_next(&mem_iter))) { 04570 if (cur->dynamic) 04571 q->membercount++; 04572 cur->status = ast_device_state(cur->interface); 04573 ao2_ref(cur, -1); 04574 } 04575 ast_mutex_unlock(&q->lock); 04576 } 04577 } 04578 AST_LIST_TRAVERSE_SAFE_END; 04579 AST_LIST_UNLOCK(&queues); 04580 return 1; 04581 }
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 3348 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().
03349 { 03350 struct call_queue *q; 03351 struct member *mem, tmpmem; 03352 int res = RES_NOSUCHQUEUE; 03353 03354 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 03355 03356 AST_LIST_LOCK(&queues); 03357 AST_LIST_TRAVERSE(&queues, q, list) { 03358 ast_mutex_lock(&q->lock); 03359 if (strcmp(q->name, queuename)) { 03360 ast_mutex_unlock(&q->lock); 03361 continue; 03362 } 03363 03364 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 03365 /* XXX future changes should beware of this assumption!! */ 03366 if (!mem->dynamic) { 03367 res = RES_NOT_DYNAMIC; 03368 ao2_ref(mem, -1); 03369 ast_mutex_unlock(&q->lock); 03370 break; 03371 } 03372 q->membercount--; 03373 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 03374 "Queue: %s\r\n" 03375 "Location: %s\r\n" 03376 "MemberName: %s\r\n", 03377 q->name, mem->interface, mem->membername); 03378 ao2_unlink(q->members, mem); 03379 ao2_ref(mem, -1); 03380 03381 if (queue_persistent_members) 03382 dump_queue_members(q); 03383 03384 res = RES_OKAY; 03385 } else { 03386 res = RES_EXISTS; 03387 } 03388 ast_mutex_unlock(&q->lock); 03389 break; 03390 } 03391 03392 if (res == RES_OKAY) 03393 remove_from_interfaces(interface); 03394 03395 AST_LIST_UNLOCK(&queues); 03396 03397 return res; 03398 }
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 1809 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().
01810 { 01811 int res; 01812 int status; 01813 char tech[256]; 01814 char *location; 01815 const char *macrocontext, *macroexten; 01816 01817 /* on entry here, we know that tmp->chan == NULL */ 01818 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) { 01819 if (queue_debug) 01820 ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface); 01821 if (qe->chan->cdr) 01822 ast_cdr_busy(qe->chan->cdr); 01823 tmp->stillgoing = 0; 01824 (*busies)++; 01825 return 0; 01826 } 01827 01828 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 01829 if (queue_debug) 01830 ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface); 01831 if (qe->chan->cdr) 01832 ast_cdr_busy(qe->chan->cdr); 01833 tmp->stillgoing = 0; 01834 return 0; 01835 } 01836 01837 if (tmp->member->paused) { 01838 if (queue_debug) 01839 ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface); 01840 if (qe->chan->cdr) 01841 ast_cdr_busy(qe->chan->cdr); 01842 tmp->stillgoing = 0; 01843 return 0; 01844 } 01845 if (use_weight && compare_weight(qe->parent,tmp->member)) { 01846 if (queue_debug) 01847 ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 01848 if (qe->chan->cdr) 01849 ast_cdr_busy(qe->chan->cdr); 01850 tmp->stillgoing = 0; 01851 (*busies)++; 01852 return 0; 01853 } 01854 01855 ast_copy_string(tech, tmp->interface, sizeof(tech)); 01856 if ((location = strchr(tech, '/'))) 01857 *location++ = '\0'; 01858 else 01859 location = ""; 01860 01861 /* Request the peer */ 01862 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); 01863 if (!tmp->chan) { /* If we can't, just go on to the next call */ 01864 if (queue_debug) 01865 ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech); 01866 if (qe->chan->cdr) 01867 ast_cdr_busy(qe->chan->cdr); 01868 tmp->stillgoing = 0; 01869 01870 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01871 01872 ast_mutex_lock(&qe->parent->lock); 01873 qe->parent->rrpos++; 01874 ast_mutex_unlock(&qe->parent->lock); 01875 01876 (*busies)++; 01877 return 0; 01878 } 01879 01880 /* Increment ring count */ 01881 tmp->member->ringcount++; 01882 tmp->chan->appl = "AppQueue"; 01883 tmp->chan->data = "(Outgoing Line)"; 01884 tmp->chan->whentohangup = 0; 01885 if (tmp->chan->cid.cid_num) 01886 free(tmp->chan->cid.cid_num); 01887 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); 01888 if (tmp->chan->cid.cid_name) 01889 free(tmp->chan->cid.cid_name); 01890 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); 01891 if (tmp->chan->cid.cid_ani) 01892 free(tmp->chan->cid.cid_ani); 01893 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); 01894 01895 /* Inherit specially named variables from parent channel */ 01896 ast_channel_inherit_variables(qe->chan, tmp->chan); 01897 01898 /* Presense of ADSI CPE on outgoing channel follows ours */ 01899 tmp->chan->adsicpe = qe->chan->adsicpe; 01900 01901 /* Inherit context and extension */ 01902 ast_channel_lock(qe->chan); 01903 macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT"); 01904 if (!ast_strlen_zero(macrocontext)) 01905 ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext)); 01906 else 01907 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext)); 01908 macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN"); 01909 if (!ast_strlen_zero(macroexten)) 01910 ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten)); 01911 else 01912 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 01913 ast_channel_unlock(qe->chan); 01914 01915 /* Place the call, but don't wait on the answer */ 01916 if ((res = ast_call(tmp->chan, location, 0))) { 01917 /* Again, keep going even if there's an error */ 01918 if (option_debug) 01919 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); 01920 if (option_verbose > 2) 01921 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface); 01922 do_hang(tmp); 01923 (*busies)++; 01924 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01925 return 0; 01926 } else if (qe->parent->eventwhencalled) { 01927 char vars[2048]; 01928 01929 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 01930 "AgentCalled: %s\r\n" 01931 "AgentName: %s\r\n" 01932 "ChannelCalling: %s\r\n" 01933 "CallerID: %s\r\n" 01934 "CallerIDName: %s\r\n" 01935 "Context: %s\r\n" 01936 "Extension: %s\r\n" 01937 "Priority: %d\r\n" 01938 "%s", 01939 tmp->interface, tmp->member->membername, qe->chan->name, 01940 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", 01941 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", 01942 qe->chan->context, qe->chan->exten, qe->chan->priority, 01943 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 01944 if (option_verbose > 2) 01945 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface); 01946 } 01947 01948 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01949 return 1; 01950 }
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 1976 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().
01977 { 01978 int ret = 0; 01979 01980 while (ret == 0) { 01981 struct callattempt *best = find_best(outgoing); 01982 if (!best) { 01983 if (option_debug) 01984 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); 01985 break; 01986 } 01987 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 01988 struct callattempt *cur; 01989 /* Ring everyone who shares this best metric (for ringall) */ 01990 for (cur = outgoing; cur; cur = cur->q_next) { 01991 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 01992 if (option_debug) 01993 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 01994 ret |= ring_entry(qe, cur, busies); 01995 } 01996 } 01997 } else { 01998 /* Ring just the best channel */ 01999 if (option_debug) 02000 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric); 02001 ret = ring_entry(qe, best, busies); 02002 } 02003 } 02004 02005 return ret; 02006 }
static void rna | ( | int | rnatime, | |
struct queue_ent * | qe, | |||
char * | interface, | |||
char * | membername | |||
) | [static] |
RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
Definition at line 2090 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.
02091 { 02092 if (option_verbose > 2) 02093 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime); 02094 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 02095 if (qe->parent->autopause) { 02096 if (!set_member_paused(qe->parent->name, interface, 1)) { 02097 if (option_verbose > 2) 02098 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); 02099 } else { 02100 if (option_verbose > 2) 02101 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 02102 } 02103 } 02104 return; 02105 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3713 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().
03714 { 03715 int res=-1; 03716 struct ast_module_user *lu; 03717 char *parse, *temppos = NULL; 03718 int priority_jump = 0; 03719 AST_DECLARE_APP_ARGS(args, 03720 AST_APP_ARG(queuename); 03721 AST_APP_ARG(interface); 03722 AST_APP_ARG(options); 03723 ); 03724 03725 03726 if (ast_strlen_zero(data)) { 03727 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n"); 03728 return -1; 03729 } 03730 03731 parse = ast_strdupa(data); 03732 03733 AST_STANDARD_APP_ARGS(args, parse); 03734 03735 lu = ast_module_user_add(chan); 03736 03737 if (ast_strlen_zero(args.interface)) { 03738 args.interface = ast_strdupa(chan->name); 03739 temppos = strrchr(args.interface, '-'); 03740 if (temppos) 03741 *temppos = '\0'; 03742 } 03743 03744 if (args.options) { 03745 if (strchr(args.options, 'j')) 03746 priority_jump = 1; 03747 } 03748 03749 switch (remove_from_queue(args.queuename, args.interface)) { 03750 case RES_OKAY: 03751 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 03752 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 03753 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 03754 res = 0; 03755 break; 03756 case RES_EXISTS: 03757 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 03758 if (priority_jump || ast_opt_priority_jumping) 03759 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 03760 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 03761 res = 0; 03762 break; 03763 case RES_NOSUCHQUEUE: 03764 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 03765 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 03766 res = 0; 03767 break; 03768 case RES_NOT_DYNAMIC: 03769 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 03770 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 03771 res = 0; 03772 break; 03773 } 03774 03775 ast_module_user_remove(lu); 03776 03777 return res; 03778 }
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 2032 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().
02033 { 02034 int res = 0; 02035 time_t now; 02036 02037 /* Get the current time */ 02038 time(&now); 02039 02040 /* Check to see if it is time to announce */ 02041 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 02042 return 0; 02043 02044 /* Stop the music on hold so we can play our own file */ 02045 ast_moh_stop(qe->chan); 02046 02047 if (option_verbose > 2) 02048 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n"); 02049 02050 /* Check to make sure we have a sound file. If not, reset to the first sound file */ 02051 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) { 02052 qe->last_periodic_announce_sound = 0; 02053 } 02054 02055 /* play the announcement */ 02056 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]); 02057 02058 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 02059 res = 0; 02060 02061 /* Resume Music on Hold if the caller is going to stay in the queue */ 02062 if (!res) 02063 ast_moh_start(qe->chan, qe->moh, NULL); 02064 02065 /* update last_periodic_announce_time */ 02066 qe->last_periodic_announce_time = now; 02067 02068 /* Update the current periodic announcement to the next announcement */ 02069 qe->last_periodic_announce_sound++; 02070 02071 return res; 02072 }
static int say_position | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1546 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().
01547 { 01548 int res = 0, avgholdmins, avgholdsecs; 01549 time_t now; 01550 01551 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ 01552 time(&now); 01553 if ((now - qe->last_pos) < 15) 01554 return 0; 01555 01556 /* If either our position has changed, or we are over the freq timer, say position */ 01557 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 01558 return 0; 01559 01560 ast_moh_stop(qe->chan); 01561 /* Say we're next, if we are */ 01562 if (qe->pos == 1) { 01563 res = play_file(qe->chan, qe->parent->sound_next); 01564 if (res) 01565 goto playout; 01566 else 01567 goto posout; 01568 } else { 01569 res = play_file(qe->chan, qe->parent->sound_thereare); 01570 if (res) 01571 goto playout; 01572 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */ 01573 if (res) 01574 goto playout; 01575 res = play_file(qe->chan, qe->parent->sound_calls); 01576 if (res) 01577 goto playout; 01578 } 01579 /* Round hold time to nearest minute */ 01580 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 01581 01582 /* If they have specified a rounding then round the seconds as well */ 01583 if (qe->parent->roundingseconds) { 01584 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 01585 avgholdsecs *= qe->parent->roundingseconds; 01586 } else { 01587 avgholdsecs = 0; 01588 } 01589 01590 if (option_verbose > 2) 01591 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 01592 01593 /* If the hold time is >1 min, if it's enabled, and if it's not 01594 supposed to be only once and we have already said it, say it */ 01595 if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime && 01596 ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) || 01597 !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) { 01598 res = play_file(qe->chan, qe->parent->sound_holdtime); 01599 if (res) 01600 goto playout; 01601 01602 if (avgholdmins > 0) { 01603 if (avgholdmins < 2) { 01604 res = play_file(qe->chan, qe->parent->sound_lessthan); 01605 if (res) 01606 goto playout; 01607 01608 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL); 01609 if (res) 01610 goto playout; 01611 } else { 01612 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 01613 if (res) 01614 goto playout; 01615 } 01616 01617 res = play_file(qe->chan, qe->parent->sound_minutes); 01618 if (res) 01619 goto playout; 01620 } 01621 if (avgholdsecs>0) { 01622 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 01623 if (res) 01624 goto playout; 01625 01626 res = play_file(qe->chan, qe->parent->sound_seconds); 01627 if (res) 01628 goto playout; 01629 } 01630 01631 } 01632 01633 posout: 01634 if (option_verbose > 2) 01635 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", 01636 qe->chan->name, qe->parent->name, qe->pos); 01637 res = play_file(qe->chan, qe->parent->sound_thanks); 01638 01639 playout: 01640 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 01641 res = 0; 01642 01643 /* Set our last_pos indicators */ 01644 qe->last_pos = now; 01645 qe->last_pos_said = qe->pos; 01646 01647 /* Don't restart music on hold if we're about to exit the caller from the queue */ 01648 if (!res) 01649 ast_moh_start(qe->chan, qe->moh, NULL); 01650 01651 return res; 01652 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
int | paused | |||
) | [static] |
Definition at line 3456 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().
03457 { 03458 int found = 0; 03459 struct call_queue *q; 03460 struct member *mem; 03461 03462 /* Special event for when all queues are paused - individual events still generated */ 03463 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 03464 if (ast_strlen_zero(queuename)) 03465 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 03466 03467 AST_LIST_LOCK(&queues); 03468 AST_LIST_TRAVERSE(&queues, q, list) { 03469 ast_mutex_lock(&q->lock); 03470 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 03471 if ((mem = interface_exists(q, interface))) { 03472 found++; 03473 if (mem->paused == paused) 03474 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 03475 mem->paused = paused; 03476 03477 if (queue_persistent_members) 03478 dump_queue_members(q); 03479 03480 if (mem->realtime) 03481 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 03482 03483 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", ""); 03484 03485 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 03486 "Queue: %s\r\n" 03487 "Location: %s\r\n" 03488 "MemberName: %s\r\n" 03489 "Paused: %d\r\n", 03490 q->name, mem->interface, mem->membername, paused); 03491 ao2_ref(mem, -1); 03492 } 03493 } 03494 ast_mutex_unlock(&q->lock); 03495 } 03496 AST_LIST_UNLOCK(&queues); 03497 03498 return found ? RESULT_SUCCESS : RESULT_FAILURE; 03499 }
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 2672 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().
02673 { 02674 struct ast_datastore *ds; 02675 struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds)); 02676 02677 if (!qtds) { 02678 ast_log(LOG_WARNING, "Memory allocation error!\n"); 02679 return NULL; 02680 } 02681 02682 ast_channel_lock(qe->chan); 02683 if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) { 02684 ast_channel_unlock(qe->chan); 02685 ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n"); 02686 return NULL; 02687 } 02688 02689 qtds->qe = qe; 02690 /* This member is refcounted in try_calling, so no need to add it here, too */ 02691 qtds->member = member; 02692 qtds->starttime = starttime; 02693 qtds->callcompletedinsl = callcompletedinsl; 02694 ds->data = qtds; 02695 ast_channel_datastore_add(qe->chan, ds); 02696 ast_channel_unlock(qe->chan); 02697 return ds; 02698 }
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 2008 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().
02009 { 02010 struct callattempt *best = find_best(outgoing); 02011 02012 if (best) { 02013 /* Ring just the best channel */ 02014 if (option_debug) 02015 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric); 02016 qe->parent->rrpos = best->metric % 1000; 02017 } else { 02018 /* Just increment rrpos */ 02019 if (qe->parent->wrapped) { 02020 /* No more channels, start over */ 02021 qe->parent->rrpos = 0; 02022 } else { 02023 /* Prioritize next entry */ 02024 qe->parent->rrpos++; 02025 } 02026 } 02027 qe->parent->wrapped = 0; 02028 02029 return 0; 02030 }
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 2725 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().
02726 { 02727 struct member *cur; 02728 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 02729 int to; 02730 char oldexten[AST_MAX_EXTENSION]=""; 02731 char oldcontext[AST_MAX_CONTEXT]=""; 02732 char queuename[256]=""; 02733 struct ast_channel *peer; 02734 struct ast_channel *which; 02735 struct callattempt *lpeer; 02736 struct member *member; 02737 struct ast_app *app; 02738 int res = 0, bridge = 0; 02739 int numbusies = 0; 02740 int x=0; 02741 char *announce = NULL; 02742 char digit = 0; 02743 time_t callstart; 02744 time_t now = time(NULL); 02745 struct ast_bridge_config bridge_config; 02746 char nondataquality = 1; 02747 char *agiexec = NULL; 02748 int ret = 0; 02749 const char *monitorfilename; 02750 const char *monitor_exec; 02751 const char *monitor_options; 02752 char tmpid[256], tmpid2[256]; 02753 char meid[1024], meid2[1024]; 02754 char mixmonargs[1512]; 02755 struct ast_app *mixmonapp = NULL; 02756 char *p; 02757 char vars[2048]; 02758 int forwardsallowed = 1; 02759 int callcompletedinsl; 02760 struct ao2_iterator memi; 02761 struct ast_datastore *datastore, *transfer_ds; 02762 02763 ast_channel_lock(qe->chan); 02764 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 02765 ast_channel_unlock(qe->chan); 02766 02767 memset(&bridge_config, 0, sizeof(bridge_config)); 02768 time(&now); 02769 02770 /* If we've already exceeded our timeout, then just stop 02771 * This should be extremely rare. queue_exec will take care 02772 * of removing the caller and reporting the timeout as the reason. 02773 */ 02774 if (qe->expire && now >= qe->expire) { 02775 res = 0; 02776 goto out; 02777 } 02778 02779 for (; options && *options; options++) 02780 switch (*options) { 02781 case 't': 02782 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 02783 break; 02784 case 'T': 02785 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 02786 break; 02787 case 'w': 02788 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 02789 break; 02790 case 'W': 02791 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 02792 break; 02793 case 'd': 02794 nondataquality = 0; 02795 break; 02796 case 'h': 02797 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 02798 break; 02799 case 'H': 02800 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 02801 break; 02802 case 'n': 02803 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) 02804 (*tries)++; 02805 else 02806 *tries = qe->parent->membercount; 02807 *noption = 1; 02808 break; 02809 case 'i': 02810 forwardsallowed = 0; 02811 break; 02812 } 02813 02814 /* Hold the lock while we setup the outgoing calls */ 02815 if (use_weight) 02816 AST_LIST_LOCK(&queues); 02817 ast_mutex_lock(&qe->parent->lock); 02818 if (option_debug) 02819 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 02820 qe->chan->name); 02821 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 02822 if (!ast_strlen_zero(qe->announce)) 02823 announce = qe->announce; 02824 if (!ast_strlen_zero(announceoverride)) 02825 announce = announceoverride; 02826 02827 memi = ao2_iterator_init(qe->parent->members, 0); 02828 while ((cur = ao2_iterator_next(&memi))) { 02829 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 02830 struct ast_dialed_interface *di; 02831 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 02832 if (!tmp) { 02833 ao2_ref(cur, -1); 02834 ast_mutex_unlock(&qe->parent->lock); 02835 if (use_weight) 02836 AST_LIST_UNLOCK(&queues); 02837 goto out; 02838 } 02839 if (!datastore) { 02840 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) { 02841 ao2_ref(cur, -1); 02842 ast_mutex_unlock(&qe->parent->lock); 02843 if (use_weight) 02844 AST_LIST_UNLOCK(&queues); 02845 free(tmp); 02846 goto out; 02847 } 02848 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 02849 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 02850 ao2_ref(cur, -1); 02851 ast_mutex_unlock(&qe->parent->lock); 02852 if (use_weight) 02853 AST_LIST_UNLOCK(&queues); 02854 free(tmp); 02855 goto out; 02856 } 02857 datastore->data = dialed_interfaces; 02858 AST_LIST_HEAD_INIT(dialed_interfaces); 02859 02860 ast_channel_lock(qe->chan); 02861 ast_channel_datastore_add(qe->chan, datastore); 02862 ast_channel_unlock(qe->chan); 02863 } else 02864 dialed_interfaces = datastore->data; 02865 02866 AST_LIST_LOCK(dialed_interfaces); 02867 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 02868 if (!strcasecmp(cur->interface, di->interface)) { 02869 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 02870 di->interface); 02871 break; 02872 } 02873 } 02874 AST_LIST_UNLOCK(dialed_interfaces); 02875 02876 if (di) { 02877 free(tmp); 02878 continue; 02879 } 02880 02881 /* It is always ok to dial a Local interface. We only keep track of 02882 * which "real" interfaces have been dialed. The Local channel will 02883 * inherit this list so that if it ends up dialing a real interface, 02884 * it won't call one that has already been called. */ 02885 if (strncasecmp(cur->interface, "Local/", 6)) { 02886 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 02887 ao2_ref(cur, -1); 02888 ast_mutex_unlock(&qe->parent->lock); 02889 if (use_weight) 02890 AST_LIST_UNLOCK(&queues); 02891 free(tmp); 02892 goto out; 02893 } 02894 strcpy(di->interface, cur->interface); 02895 02896 AST_LIST_LOCK(dialed_interfaces); 02897 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 02898 AST_LIST_UNLOCK(dialed_interfaces); 02899 } 02900 02901 tmp->stillgoing = -1; 02902 tmp->member = cur; 02903 tmp->oldstatus = cur->status; 02904 tmp->lastcall = cur->lastcall; 02905 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 02906 if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) { 02907 cur->ringcount = 0; 02908 } 02909 /* Special case: If we ring everyone, go ahead and ring them, otherwise 02910 just calculate their metric for the appropriate strategy */ 02911 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 02912 /* Put them in the list of outgoing thingies... We're ready now. 02913 XXX If we're forcibly removed, these outgoing calls won't get 02914 hung up XXX */ 02915 tmp->q_next = outgoing; 02916 outgoing = tmp; 02917 /* If this line is up, don't try anybody else */ 02918 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 02919 break; 02920 } else { 02921 ao2_ref(cur, -1); 02922 free(tmp); 02923 } 02924 } 02925 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 02926 to = (qe->expire - now) * 1000; 02927 else 02928 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 02929 ++qe->pending; 02930 ++qe->tries; 02931 if (option_debug) 02932 ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n", 02933 qe->chan->name, queuename, qe->tries); 02934 ast_mutex_unlock(&qe->parent->lock); 02935 ring_one(qe, outgoing, &numbusies); 02936 if (use_weight) 02937 AST_LIST_UNLOCK(&queues); 02938 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); 02939 /* The ast_channel_datastore_remove() function could fail here if the 02940 * datastore was moved to another channel during a masquerade. If this is 02941 * the case, don't free the datastore here because later, when the channel 02942 * to which the datastore was moved hangs up, it will attempt to free this 02943 * datastore again, causing a crash 02944 */ 02945 ast_channel_lock(qe->chan); 02946 if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) { 02947 ast_channel_datastore_free(datastore); 02948 } 02949 ast_channel_unlock(qe->chan); 02950 ast_mutex_lock(&qe->parent->lock); 02951 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { 02952 store_next(qe, outgoing); 02953 } 02954 ast_mutex_unlock(&qe->parent->lock); 02955 peer = lpeer ? lpeer->chan : NULL; 02956 if (!peer) { 02957 qe->pending = 0; 02958 if (to) { 02959 /* Must gotten hung up */ 02960 res = -1; 02961 } else { 02962 /* User exited by pressing a digit */ 02963 res = digit; 02964 } 02965 if (res == -1) { 02966 /* Post this CDR, and mark call as NOANSWER */ 02967 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH); 02968 ast_cdr_noanswer(qe->chan->cdr); 02969 if (queue_debug) 02970 ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name); 02971 } 02972 if (qe->parent->eventwhencalled) { 02973 manager_event(EVENT_FLAG_AGENT, "AgentTimeout", 02974 "Queue: %s\r\n" 02975 "ChannelCalling: %s\r\n" 02976 "Uniqueid: %s\r\n" 02977 "Tries: %d\r\n" 02978 "Holdtime: %ld\r\n", 02979 queuename, qe->chan->name, qe->chan->uniqueid, qe->tries, 02980 (long)time(NULL) - qe->start); 02981 } 02982 } else { /* peer is valid */ 02983 /* Ah ha! Someone answered within the desired timeframe. Of course after this 02984 we will always return with -1 so that it is hung up properly after the 02985 conversation. */ 02986 if (!strcmp(qe->chan->tech->type, "Zap")) 02987 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 02988 if (!strcmp(peer->tech->type, "Zap")) 02989 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 02990 /* Update parameters for the queue */ 02991 time(&now); 02992 recalc_holdtime(qe, (now - qe->start)); 02993 ast_mutex_lock(&qe->parent->lock); 02994 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 02995 ast_mutex_unlock(&qe->parent->lock); 02996 member = lpeer->member; 02997 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 02998 ao2_ref(member, 1); 02999 hangupcalls(outgoing, peer); 03000 outgoing = NULL; 03001 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 03002 int res2; 03003 03004 res2 = ast_autoservice_start(qe->chan); 03005 if (!res2) { 03006 if (qe->parent->memberdelay) { 03007 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 03008 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 03009 } 03010 if (!res2 && announce) { 03011 play_file(peer, announce); 03012 } 03013 if (!res2 && qe->parent->reportholdtime) { 03014 if (!play_file(peer, qe->parent->sound_reporthold)) { 03015 int holdtime; 03016 03017 time(&now); 03018 holdtime = abs((now - qe->start) / 60); 03019 if (holdtime < 2) { 03020 play_file(peer, qe->parent->sound_lessthan); 03021 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL); 03022 } else 03023 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 03024 play_file(peer, qe->parent->sound_minutes); 03025 } 03026 } 03027 } 03028 res2 |= ast_autoservice_stop(qe->chan); 03029 if (peer->_softhangup) { 03030 /* Agent must have hung up */ 03031 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 03032 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 03033 if (qe->parent->eventwhencalled) 03034 manager_event(EVENT_FLAG_AGENT, "AgentDump", 03035 "Queue: %s\r\n" 03036 "Uniqueid: %s\r\n" 03037 "Channel: %s\r\n" 03038 "Member: %s\r\n" 03039 "MemberName: %s\r\n" 03040 "%s", 03041 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03042 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03043 ast_hangup(peer); 03044 ao2_ref(member, -1); 03045 goto out; 03046 } else if (res2) { 03047 /* Caller must have hung up just before being connected*/ 03048 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 03049 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 03050 record_abandoned(qe); 03051 ast_hangup(peer); 03052 ao2_ref(member, -1); 03053 return -1; 03054 } 03055 } 03056 /* Stop music on hold */ 03057 ast_moh_stop(qe->chan); 03058 /* If appropriate, log that we have a destination channel */ 03059 if (qe->chan->cdr) 03060 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 03061 /* Make sure channels are compatible */ 03062 res = ast_channel_make_compatible(qe->chan, peer); 03063 if (res < 0) { 03064 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 03065 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 03066 record_abandoned(qe); 03067 ast_hangup(peer); 03068 ao2_ref(member, -1); 03069 return -1; 03070 } 03071 03072 if (qe->parent->setinterfacevar) 03073 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface); 03074 03075 /* Begin Monitoring */ 03076 if (qe->parent->monfmt && *qe->parent->monfmt) { 03077 if (!qe->parent->montype) { 03078 if (option_debug) 03079 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n"); 03080 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 03081 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) 03082 which = qe->chan; 03083 else 03084 which = peer; 03085 if (monitorfilename) 03086 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); 03087 else if (qe->chan->cdr) 03088 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); 03089 else { 03090 /* Last ditch effort -- no CDR, make up something */ 03091 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03092 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 ); 03093 } 03094 if (qe->parent->monjoin) 03095 ast_monitor_setjoinfiles(which, 1); 03096 } else { 03097 if (option_debug) 03098 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n"); 03099 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 03100 if (!monitorfilename) { 03101 if (qe->chan->cdr) 03102 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1); 03103 else 03104 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 03105 } else { 03106 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1); 03107 for (p = tmpid2; *p ; p++) { 03108 if (*p == '^' && *(p+1) == '{') { 03109 *p = '$'; 03110 } 03111 } 03112 03113 memset(tmpid, 0, sizeof(tmpid)); 03114 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 03115 } 03116 03117 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"); 03118 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"); 03119 03120 if (monitor_exec) { 03121 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1); 03122 for (p = meid2; *p ; p++) { 03123 if (*p == '^' && *(p+1) == '{') { 03124 *p = '$'; 03125 } 03126 } 03127 03128 memset(meid, 0, sizeof(meid)); 03129 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 03130 } 03131 03132 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt); 03133 03134 mixmonapp = pbx_findapp("MixMonitor"); 03135 03136 if (strchr(tmpid2, '|')) { 03137 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n"); 03138 mixmonapp = NULL; 03139 } 03140 03141 if (!monitor_options) 03142 monitor_options = ""; 03143 03144 if (strchr(monitor_options, '|')) { 03145 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n"); 03146 mixmonapp = NULL; 03147 } 03148 03149 if (mixmonapp) { 03150 if (!ast_strlen_zero(monitor_exec)) 03151 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec); 03152 else 03153 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options); 03154 03155 if (option_debug) 03156 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 03157 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 03158 if (qe->chan->cdr) 03159 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03160 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 03161 if (qe->chan->cdr) 03162 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 03163 03164 } else 03165 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 03166 03167 } 03168 } 03169 /* Drop out of the queue at this point, to prepare for next caller */ 03170 leave_queue(qe); 03171 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 03172 if (option_debug) 03173 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); 03174 ast_channel_sendurl(peer, url); 03175 } 03176 if (!ast_strlen_zero(agi)) { 03177 if (option_debug) 03178 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi); 03179 app = pbx_findapp("agi"); 03180 if (app) { 03181 agiexec = ast_strdupa(agi); 03182 ret = pbx_exec(qe->chan, app, agiexec); 03183 } else 03184 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 03185 } 03186 qe->handled++; 03187 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid); 03188 if (qe->parent->eventwhencalled) 03189 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 03190 "Queue: %s\r\n" 03191 "Uniqueid: %s\r\n" 03192 "Channel: %s\r\n" 03193 "Member: %s\r\n" 03194 "MemberName: %s\r\n" 03195 "Holdtime: %ld\r\n" 03196 "BridgedChannel: %s\r\n" 03197 "%s", 03198 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03199 (long)time(NULL) - qe->start, peer->uniqueid, 03200 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03201 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 03202 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 03203 time(&callstart); 03204 03205 if (member->status == AST_DEVICE_NOT_INUSE) 03206 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); 03207 03208 transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl); 03209 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 03210 03211 ast_channel_lock(qe->chan); 03212 if (!attended_transfer_occurred(qe->chan)) { 03213 struct ast_datastore *tds; 03214 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) { 03215 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 03216 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 03217 (long) (time(NULL) - callstart)); 03218 } else if (qe->chan->_softhangup) { 03219 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 03220 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03221 if (qe->parent->eventwhencalled) 03222 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03223 "Queue: %s\r\n" 03224 "Uniqueid: %s\r\n" 03225 "Channel: %s\r\n" 03226 "Member: %s\r\n" 03227 "MemberName: %s\r\n" 03228 "HoldTime: %ld\r\n" 03229 "TalkTime: %ld\r\n" 03230 "Reason: caller\r\n" 03231 "%s", 03232 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03233 (long)(callstart - qe->start), (long)(time(NULL) - callstart), 03234 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03235 } else { 03236 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 03237 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03238 if (qe->parent->eventwhencalled) 03239 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03240 "Queue: %s\r\n" 03241 "Uniqueid: %s\r\n" 03242 "Channel: %s\r\n" 03243 "MemberName: %s\r\n" 03244 "HoldTime: %ld\r\n" 03245 "TalkTime: %ld\r\n" 03246 "Reason: agent\r\n" 03247 "%s", 03248 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start), 03249 (long)(time(NULL) - callstart), 03250 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03251 } 03252 if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) { 03253 ast_channel_datastore_remove(qe->chan, tds); 03254 } 03255 update_queue(qe->parent, member, callcompletedinsl); 03256 } 03257 03258 if (transfer_ds) { 03259 ast_channel_datastore_free(transfer_ds); 03260 } 03261 ast_channel_unlock(qe->chan); 03262 ast_hangup(peer); 03263 res = bridge ? bridge : 1; 03264 ao2_ref(member, -1); 03265 } 03266 out: 03267 hangupcalls(outgoing, NULL); 03268 03269 return res; 03270 }
static int unload_module | ( | void | ) | [static] |
Definition at line 5182 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().
05183 { 05184 int res; 05185 05186 if (device_state.thread != AST_PTHREADT_NULL) { 05187 device_state.stop = 1; 05188 ast_mutex_lock(&device_state.lock); 05189 ast_cond_signal(&device_state.cond); 05190 ast_mutex_unlock(&device_state.lock); 05191 pthread_join(device_state.thread, NULL); 05192 } 05193 05194 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 05195 res = ast_manager_unregister("QueueStatus"); 05196 res |= ast_manager_unregister("Queues"); 05197 res |= ast_manager_unregister("QueueAdd"); 05198 res |= ast_manager_unregister("QueueRemove"); 05199 res |= ast_manager_unregister("QueuePause"); 05200 res |= ast_unregister_application(app_aqm); 05201 res |= ast_unregister_application(app_rqm); 05202 res |= ast_unregister_application(app_pqm); 05203 res |= ast_unregister_application(app_upqm); 05204 res |= ast_unregister_application(app_ql); 05205 res |= ast_unregister_application(app); 05206 res |= ast_custom_function_unregister(&queueagentcount_function); 05207 res |= ast_custom_function_unregister(&queuemembercount_function); 05208 res |= ast_custom_function_unregister(&queuememberlist_function); 05209 res |= ast_custom_function_unregister(&queuewaitingcount_function); 05210 ast_devstate_del(statechange_queue, NULL); 05211 05212 ast_module_user_hangup_all(); 05213 05214 clear_and_free_interfaces(); 05215 05216 return res; 05217 }
static int update_queue | ( | struct call_queue * | q, | |
struct member * | member, | |||
int | callcompletedinsl | |||
) | [static] |
Definition at line 2525 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().
02526 { 02527 ast_mutex_lock(&q->lock); 02528 time(&member->lastcall); 02529 member->calls++; 02530 q->callscompleted++; 02531 if (callcompletedinsl) 02532 q->callscompletedinsl++; 02533 ast_mutex_unlock(&q->lock); 02534 return 0; 02535 }
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 3655 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().
03656 { 03657 struct ast_module_user *lu; 03658 char *parse; 03659 int priority_jump = 0; 03660 int ignore_fail = 0; 03661 AST_DECLARE_APP_ARGS(args, 03662 AST_APP_ARG(queuename); 03663 AST_APP_ARG(interface); 03664 AST_APP_ARG(options); 03665 ); 03666 03667 if (ast_strlen_zero(data)) { 03668 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03669 return -1; 03670 } 03671 03672 parse = ast_strdupa(data); 03673 03674 AST_STANDARD_APP_ARGS(args, parse); 03675 03676 lu = ast_module_user_add(chan); 03677 03678 if (args.options) { 03679 if (strchr(args.options, 'j')) 03680 priority_jump = 1; 03681 if (strchr(args.options, 'i')) 03682 ignore_fail = 1; 03683 } 03684 03685 if (ast_strlen_zero(args.interface)) { 03686 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03687 ast_module_user_remove(lu); 03688 return -1; 03689 } 03690 03691 if (set_member_paused(args.queuename, args.interface, 0)) { 03692 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 03693 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 03694 if (priority_jump || ast_opt_priority_jumping) { 03695 if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03696 ast_module_user_remove(lu); 03697 return 0; 03698 } 03699 } 03700 ast_module_user_remove(lu); 03701 if (ignore_fail) { 03702 return 0; 03703 } else { 03704 return -1; 03705 } 03706 } 03707 03708 ast_module_user_remove(lu); 03709 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 03710 return 0; 03711 }
static int valid_exit | ( | struct queue_ent * | qe, | |
char | digit | |||
) | [static] |
Definition at line 1513 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().
01514 { 01515 int digitlen = strlen(qe->digits); 01516 01517 /* Prevent possible buffer overflow */ 01518 if (digitlen < sizeof(qe->digits) - 2) { 01519 qe->digits[digitlen] = digit; 01520 qe->digits[digitlen + 1] = '\0'; 01521 } else { 01522 qe->digits[0] = '\0'; 01523 return 0; 01524 } 01525 01526 /* If there's no context to goto, short-circuit */ 01527 if (ast_strlen_zero(qe->context)) 01528 return 0; 01529 01530 /* If the extension is bad, then reset the digits to blank */ 01531 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) { 01532 qe->digits[0] = '\0'; 01533 return 0; 01534 } 01535 01536 /* We have an exact match */ 01537 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 01538 qe->valid_digits = 1; 01539 /* Return 1 on a successful goto */ 01540 return 1; 01541 } 01542 01543 return 0; 01544 }
static char* vars2manager | ( | struct ast_channel * | chan, | |
char * | vars, | |||
size_t | len | |||
) | [static] |
Definition at line 1769 of file app_queue.c.
References ast_copy_string(), and pbx_builtin_serialize_variables().
Referenced by ring_entry(), and try_calling().
01770 { 01771 char *tmp = alloca(len); 01772 01773 if (pbx_builtin_serialize_variables(chan, tmp, len)) { 01774 int i, j; 01775 01776 /* convert "\n" to "\nVariable: " */ 01777 strcpy(vars, "Variable: "); 01778 01779 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 01780 vars[j] = tmp[i]; 01781 01782 if (tmp[i + 1] == '\0') 01783 break; 01784 if (tmp[i] == '\n') { 01785 vars[j++] = '\r'; 01786 vars[j++] = '\n'; 01787 01788 ast_copy_string(&(vars[j]), "Variable: ", len - j); 01789 j += 9; 01790 } 01791 } 01792 if (j > len - 3) 01793 j = len - 3; 01794 vars[j++] = '\r'; 01795 vars[j++] = '\n'; 01796 vars[j] = '\0'; 01797 } else { 01798 /* there are no channel variables; leave it blank */ 01799 *vars = '\0'; 01800 } 01801 return vars; 01802 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 3272 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
03273 { 03274 /* Don't need to hold the lock while we setup the outgoing calls */ 03275 int retrywait = qe->parent->retry * 1000; 03276 03277 int res = ast_waitfordigit(qe->chan, retrywait); 03278 if (res > 0 && !valid_exit(qe, res)) 03279 res = 0; 03280 03281 return res; 03282 }
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 2118 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.
02119 { 02120 char *queue = qe->parent->name; 02121 struct callattempt *o, *start = NULL, *prev = NULL; 02122 int status; 02123 int numbusies = prebusies; 02124 int numnochan = 0; 02125 int stillgoing = 0; 02126 int orig = *to; 02127 struct ast_frame *f; 02128 struct callattempt *peer = NULL; 02129 struct ast_channel *winner; 02130 struct ast_channel *in = qe->chan; 02131 char on[80] = ""; 02132 char membername[80] = ""; 02133 long starttime = 0; 02134 long endtime = 0; 02135 02136 starttime = (long) time(NULL); 02137 02138 while (*to && !peer) { 02139 int numlines, retry, pos = 1; 02140 struct ast_channel *watchers[AST_MAX_WATCHERS]; 02141 watchers[0] = in; 02142 start = NULL; 02143 02144 for (retry = 0; retry < 2; retry++) { 02145 numlines = 0; 02146 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 02147 if (o->stillgoing) { /* Keep track of important channels */ 02148 stillgoing = 1; 02149 if (o->chan) { 02150 watchers[pos++] = o->chan; 02151 if (!start) 02152 start = o; 02153 else 02154 prev->call_next = o; 02155 prev = o; 02156 } 02157 } 02158 numlines++; 02159 } 02160 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 02161 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 02162 break; 02163 /* On "ringall" strategy we only move to the next penalty level 02164 when *all* ringing phones are done in the current penalty level */ 02165 ring_one(qe, outgoing, &numbusies); 02166 /* and retry... */ 02167 } 02168 if (pos == 1 /* not found */) { 02169 if (numlines == (numbusies + numnochan)) { 02170 ast_log(LOG_DEBUG, "Everyone is busy at this time\n"); 02171 } else { 02172 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan); 02173 } 02174 *to = 0; 02175 return NULL; 02176 } 02177 winner = ast_waitfor_n(watchers, pos, to); 02178 for (o = start; o; o = o->call_next) { 02179 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 02180 if (!peer) { 02181 if (option_verbose > 2) 02182 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02183 peer = o; 02184 } 02185 } else if (o->chan && (o->chan == winner)) { 02186 02187 ast_copy_string(on, o->member->interface, sizeof(on)); 02188 ast_copy_string(membername, o->member->membername, sizeof(membername)); 02189 02190 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 02191 if (option_verbose > 2) 02192 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); 02193 numnochan++; 02194 do_hang(o); 02195 winner = NULL; 02196 continue; 02197 } else if (!ast_strlen_zero(o->chan->call_forward)) { 02198 char tmpchan[256]; 02199 char *stuff; 02200 char *tech; 02201 02202 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 02203 if ((stuff = strchr(tmpchan, '/'))) { 02204 *stuff++ = '\0'; 02205 tech = tmpchan; 02206 } else { 02207 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 02208 stuff = tmpchan; 02209 tech = "Local"; 02210 } 02211 /* Before processing channel, go ahead and check for forwarding */ 02212 if (option_verbose > 2) 02213 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); 02214 /* Setup parameters */ 02215 o->chan = ast_request(tech, in->nativeformats, stuff, &status); 02216 if (!o->chan) { 02217 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); 02218 o->stillgoing = 0; 02219 numnochan++; 02220 } else { 02221 ast_channel_inherit_variables(in, o->chan); 02222 ast_channel_datastore_inherit(in, o->chan); 02223 if (o->chan->cid.cid_num) 02224 free(o->chan->cid.cid_num); 02225 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); 02226 02227 if (o->chan->cid.cid_name) 02228 free(o->chan->cid.cid_name); 02229 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); 02230 02231 ast_string_field_set(o->chan, accountcode, in->accountcode); 02232 o->chan->cdrflags = in->cdrflags; 02233 02234 if (in->cid.cid_ani) { 02235 if (o->chan->cid.cid_ani) 02236 free(o->chan->cid.cid_ani); 02237 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); 02238 } 02239 if (o->chan->cid.cid_rdnis) 02240 free(o->chan->cid.cid_rdnis); 02241 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); 02242 if (ast_call(o->chan, tmpchan, 0)) { 02243 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); 02244 do_hang(o); 02245 numnochan++; 02246 } 02247 } 02248 /* Hangup the original channel now, in case we needed it */ 02249 ast_hangup(winner); 02250 continue; 02251 } 02252 f = ast_read(winner); 02253 if (f) { 02254 if (f->frametype == AST_FRAME_CONTROL) { 02255 switch (f->subclass) { 02256 case AST_CONTROL_ANSWER: 02257 /* This is our guy if someone answered. */ 02258 if (!peer) { 02259 if (option_verbose > 2) 02260 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02261 peer = o; 02262 } 02263 break; 02264 case AST_CONTROL_BUSY: 02265 if (option_verbose > 2) 02266 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); 02267 if (in->cdr) 02268 ast_cdr_busy(in->cdr); 02269 do_hang(o); 02270 endtime = (long)time(NULL); 02271 endtime -= starttime; 02272 rna(endtime*1000, qe, on, membername); 02273 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02274 if (qe->parent->timeoutrestart) 02275 *to = orig; 02276 ring_one(qe, outgoing, &numbusies); 02277 } 02278 numbusies++; 02279 break; 02280 case AST_CONTROL_CONGESTION: 02281 if (option_verbose > 2) 02282 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name); 02283 if (in->cdr) 02284 ast_cdr_busy(in->cdr); 02285 endtime = (long)time(NULL); 02286 endtime -= starttime; 02287 rna(endtime*1000, qe, on, membername); 02288 do_hang(o); 02289 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02290 if (qe->parent->timeoutrestart) 02291 *to = orig; 02292 ring_one(qe, outgoing, &numbusies); 02293 } 02294 numbusies++; 02295 break; 02296 case AST_CONTROL_RINGING: 02297 if (option_verbose > 2) 02298 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); 02299 break; 02300 case AST_CONTROL_OFFHOOK: 02301 /* Ignore going off hook */ 02302 break; 02303 default: 02304 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); 02305 } 02306 } 02307 ast_frfree(f); 02308 } else { 02309 endtime = (long) time(NULL) - starttime; 02310 rna(endtime * 1000, qe, on, membername); 02311 do_hang(o); 02312 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02313 if (qe->parent->timeoutrestart) 02314 *to = orig; 02315 ring_one(qe, outgoing, &numbusies); 02316 } 02317 } 02318 } 02319 } 02320 if (winner == in) { 02321 f = ast_read(in); 02322 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02323 /* Got hung up */ 02324 *to = -1; 02325 if (f) 02326 ast_frfree(f); 02327 return NULL; 02328 } 02329 /* First check if DTMF digit is a valid exit */ 02330 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) { 02331 if (option_verbose > 3) 02332 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass); 02333 *to = 0; 02334 *digit = f->subclass; 02335 ast_frfree(f); 02336 return NULL; 02337 } 02338 /* Else check if DTMF should be interpreted as caller disconnect */ 02339 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { 02340 if (option_verbose > 3) 02341 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); 02342 *to = 0; 02343 ast_frfree(f); 02344 return NULL; 02345 } 02346 ast_frfree(f); 02347 } 02348 if (!*to) { 02349 for (o = start; o; o = o->call_next) 02350 rna(orig, qe, o->interface, o->member->membername); 02351 } 02352 } 02353 02354 return peer; 02355 }
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 2450 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().
02451 { 02452 int res = 0; 02453 02454 /* This is the holding pen for callers 2 through maxlen */ 02455 for (;;) { 02456 enum queue_member_status stat; 02457 02458 if (is_our_turn(qe)) 02459 break; 02460 02461 /* If we have timed out, break out */ 02462 if (qe->expire && (time(NULL) >= qe->expire)) { 02463 *reason = QUEUE_TIMEOUT; 02464 break; 02465 } 02466 02467 stat = get_member_status(qe->parent, qe->max_penalty); 02468 02469 /* leave the queue if no agents, if enabled */ 02470 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 02471 *reason = QUEUE_LEAVEEMPTY; 02472 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02473 leave_queue(qe); 02474 break; 02475 } 02476 02477 /* leave the queue if no reachable agents, if enabled */ 02478 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 02479 *reason = QUEUE_LEAVEUNAVAIL; 02480 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02481 leave_queue(qe); 02482 break; 02483 } 02484 02485 /* Make a position announcement, if enabled */ 02486 if (qe->parent->announcefrequency && !ringing && 02487 (res = say_position(qe))) 02488 break; 02489 02490 /* If we have timed out, break out */ 02491 if (qe->expire && (time(NULL) >= qe->expire)) { 02492 *reason = QUEUE_TIMEOUT; 02493 break; 02494 } 02495 02496 /* Make a periodic announcement, if enabled */ 02497 if (qe->parent->periodicannouncefrequency && !ringing && 02498 (res = say_periodic_announcement(qe))) 02499 break; 02500 02501 /* If we have timed out, break out */ 02502 if (qe->expire && (time(NULL) >= qe->expire)) { 02503 *reason = QUEUE_TIMEOUT; 02504 break; 02505 } 02506 02507 /* Wait a second before checking again */ 02508 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 02509 if (res > 0 && !valid_exit(qe, res)) 02510 res = 0; 02511 else 02512 break; 02513 } 02514 02515 /* If we have timed out, break out */ 02516 if (qe->expire && (time(NULL) >= qe->expire)) { 02517 *reason = QUEUE_TIMEOUT; 02518 break; 02519 } 02520 } 02521 02522 return res; 02523 }
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 5264 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 5264 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 5153 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 5158 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 5148 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 5142 of file app_queue.c.
char qrm_cmd_usage[] [static] |
Initial value:
"Usage: queue remove member <channel> from <queue>\n"
Definition at line 5145 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 5138 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 2619 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] |