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