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