#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"
Include dependency graph for app_queue.c:
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 | 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 | 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 | 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_qac (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 | 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 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_persistent_members = 0 |
queues.conf [general] option | |
struct { | |
enum queue_result id | |
char * text | |
} | queue_results [] |
static char | queue_show_usage [] |
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 2064 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 261 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 368 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 371 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 queue_member_status |
Definition at line 519 of file app_queue.c.
00519 { 00520 QUEUE_NO_MEMBERS, 00521 QUEUE_NO_REACHABLE_MEMBERS, 00522 QUEUE_NORMAL 00523 };
enum queue_result |
QUEUE_UNKNOWN | |
QUEUE_TIMEOUT | |
QUEUE_JOINEMPTY | |
QUEUE_LEAVEEMPTY | |
QUEUE_JOINUNAVAIL | |
QUEUE_LEAVEUNAVAIL | |
QUEUE_FULL |
Definition at line 275 of file app_queue.c.
00275 { 00276 QUEUE_UNKNOWN = 0, 00277 QUEUE_TIMEOUT = 1, 00278 QUEUE_JOINEMPTY = 2, 00279 QUEUE_LEAVEEMPTY = 3, 00280 QUEUE_JOINUNAVAIL = 4, 00281 QUEUE_LEAVEUNAVAIL = 5, 00282 QUEUE_FULL = 6, 00283 };
static int __queues_show | ( | struct mansession * | s, | |
int | manager, | |||
int | fd, | |||
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 4279 of file app_queue.c.
References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_build_string(), ast_cli(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), 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::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, s, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.
Referenced by manager_queues_show(), and queue_show().
04280 { 04281 struct call_queue *q; 04282 struct queue_ent *qe; 04283 struct member *mem; 04284 int pos, queue_show; 04285 time_t now; 04286 char max_buf[80]; 04287 char *max; 04288 size_t max_left; 04289 float sl = 0; 04290 char *term = manager ? "\r\n" : "\n"; 04291 struct ao2_iterator mem_iter; 04292 04293 time(&now); 04294 if (argc == 2) 04295 queue_show = 0; 04296 else if (argc == 3) 04297 queue_show = 1; 04298 else 04299 return RESULT_SHOWUSAGE; 04300 04301 /* We only want to load realtime queues when a specific queue is asked for. */ 04302 if (queue_show) 04303 load_realtime_queue(argv[2]); 04304 04305 AST_LIST_LOCK(&queues); 04306 if (AST_LIST_EMPTY(&queues)) { 04307 AST_LIST_UNLOCK(&queues); 04308 if (queue_show) { 04309 if (s) 04310 astman_append(s, "No such queue: %s.%s",argv[2], term); 04311 else 04312 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 04313 } else { 04314 if (s) 04315 astman_append(s, "No queues.%s", term); 04316 else 04317 ast_cli(fd, "No queues.%s", term); 04318 } 04319 return RESULT_SUCCESS; 04320 } 04321 AST_LIST_TRAVERSE(&queues, q, list) { 04322 ast_mutex_lock(&q->lock); 04323 if (queue_show) { 04324 if (strcasecmp(q->name, argv[2]) != 0) { 04325 ast_mutex_unlock(&q->lock); 04326 if (!AST_LIST_NEXT(q, list)) { 04327 ast_cli(fd, "No such queue: %s.%s",argv[2], term); 04328 break; 04329 } 04330 continue; 04331 } 04332 } 04333 max_buf[0] = '\0'; 04334 max = max_buf; 04335 max_left = sizeof(max_buf); 04336 if (q->maxlen) 04337 ast_build_string(&max, &max_left, "%d", q->maxlen); 04338 else 04339 ast_build_string(&max, &max_left, "unlimited"); 04340 sl = 0; 04341 if (q->callscompleted > 0) 04342 sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted); 04343 if (s) 04344 astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", 04345 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, 04346 q->callscompleted, q->callsabandoned,sl,q->servicelevel, term); 04347 else 04348 ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s", 04349 q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->weight, q->callscompleted, q->callsabandoned,sl,q->servicelevel, term); 04350 if (ao2_container_count(q->members)) { 04351 if (s) 04352 astman_append(s, " Members: %s", term); 04353 else 04354 ast_cli(fd, " Members: %s", term); 04355 mem_iter = ao2_iterator_init(q->members, 0); 04356 while ((mem = ao2_iterator_next(&mem_iter))) { 04357 max_buf[0] = '\0'; 04358 max = max_buf; 04359 max_left = sizeof(max_buf); 04360 if (mem->penalty) 04361 ast_build_string(&max, &max_left, " with penalty %d", mem->penalty); 04362 if (mem->dynamic) 04363 ast_build_string(&max, &max_left, " (dynamic)"); 04364 if (mem->realtime) 04365 ast_build_string(&max, &max_left, " (realtime)"); 04366 if (mem->paused) 04367 ast_build_string(&max, &max_left, " (paused)"); 04368 ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status)); 04369 if (mem->calls) { 04370 ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)", 04371 mem->calls, (long) (time(NULL) - mem->lastcall)); 04372 } else 04373 ast_build_string(&max, &max_left, " has taken no calls yet"); 04374 if (s) 04375 astman_append(s, " %s%s%s", mem->membername, max_buf, term); 04376 else 04377 ast_cli(fd, " %s%s%s", mem->membername, max_buf, term); 04378 ao2_ref(mem, -1); 04379 } 04380 } else if (s) 04381 astman_append(s, " No Members%s", term); 04382 else 04383 ast_cli(fd, " No Members%s", term); 04384 if (q->head) { 04385 pos = 1; 04386 if (s) 04387 astman_append(s, " Callers: %s", term); 04388 else 04389 ast_cli(fd, " Callers: %s", term); 04390 for (qe = q->head; qe; qe = qe->next) { 04391 if (s) 04392 astman_append(s, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", 04393 pos++, qe->chan->name, (long) (now - qe->start) / 60, 04394 (long) (now - qe->start) % 60, qe->prio, term); 04395 else 04396 ast_cli(fd, " %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++, 04397 qe->chan->name, (long) (now - qe->start) / 60, 04398 (long) (now - qe->start) % 60, qe->prio, term); 04399 } 04400 } else if (s) 04401 astman_append(s, " No Callers%s", term); 04402 else 04403 ast_cli(fd, " No Callers%s", term); 04404 if (s) 04405 astman_append(s, "%s", term); 04406 else 04407 ast_cli(fd, "%s", term); 04408 ast_mutex_unlock(&q->lock); 04409 if (queue_show) 04410 break; 04411 } 04412 AST_LIST_UNLOCK(&queues); 04413 return RESULT_SUCCESS; 04414 }
static int add_to_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 861 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().
00862 { 00863 struct member_interface *curint; 00864 00865 AST_LIST_LOCK(&interfaces); 00866 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00867 if (!strcasecmp(curint->interface, interface)) 00868 break; 00869 } 00870 00871 if (curint) { 00872 AST_LIST_UNLOCK(&interfaces); 00873 return 0; 00874 } 00875 00876 if (option_debug) 00877 ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface); 00878 00879 if ((curint = ast_calloc(1, sizeof(*curint)))) { 00880 ast_copy_string(curint->interface, interface, sizeof(curint->interface)); 00881 AST_LIST_INSERT_HEAD(&interfaces, curint, list); 00882 } 00883 AST_LIST_UNLOCK(&interfaces); 00884 00885 return 0; 00886 }
static int add_to_queue | ( | const char * | queuename, | |
const char * | interface, | |||
const char * | membername, | |||
int | penalty, | |||
int | paused, | |||
int | dump | |||
) | [static] |
Definition at line 3187 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().
03188 { 03189 struct call_queue *q; 03190 struct member *new_member, *old_member; 03191 int res = RES_NOSUCHQUEUE; 03192 03193 /* \note Ensure the appropriate realtime queue is loaded. Note that this 03194 * short-circuits if the queue is already in memory. */ 03195 if (!(q = load_realtime_queue(queuename))) 03196 return res; 03197 03198 AST_LIST_LOCK(&queues); 03199 03200 ast_mutex_lock(&q->lock); 03201 if ((old_member = interface_exists(q, interface)) == NULL) { 03202 add_to_interfaces(interface); 03203 if ((new_member = create_queue_member(interface, membername, penalty, paused))) { 03204 new_member->dynamic = 1; 03205 ao2_link(q->members, new_member); 03206 q->membercount++; 03207 manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded", 03208 "Queue: %s\r\n" 03209 "Location: %s\r\n" 03210 "MemberName: %s\r\n" 03211 "Membership: %s\r\n" 03212 "Penalty: %d\r\n" 03213 "CallsTaken: %d\r\n" 03214 "LastCall: %d\r\n" 03215 "Status: %d\r\n" 03216 "Paused: %d\r\n", 03217 q->name, new_member->interface, new_member->membername, 03218 "dynamic", 03219 new_member->penalty, new_member->calls, (int) new_member->lastcall, 03220 new_member->status, new_member->paused); 03221 03222 ao2_ref(new_member, -1); 03223 new_member = NULL; 03224 03225 if (dump) 03226 dump_queue_members(q); 03227 03228 res = RES_OKAY; 03229 } else { 03230 res = RES_OUTOFMEMORY; 03231 } 03232 } else { 03233 ao2_ref(old_member, -1); 03234 res = RES_EXISTS; 03235 } 03236 ast_mutex_unlock(&q->lock); 03237 AST_LIST_UNLOCK(&queues); 03238 03239 return res; 03240 }
static struct call_queue* alloc_queue | ( | const char * | queuename | ) | [static] |
Definition at line 762 of file app_queue.c.
References ast_calloc, and ast_mutex_init().
Referenced by find_queue_by_name_rt(), and reload_queues().
00763 { 00764 struct call_queue *q; 00765 00766 if ((q = ast_calloc(1, sizeof(*q)))) { 00767 ast_mutex_init(&q->lock); 00768 ast_copy_string(q->name, queuename, sizeof(q->name)); 00769 } 00770 return q; 00771 }
static int aqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3556 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().
03557 { 03558 int res=-1; 03559 struct ast_module_user *lu; 03560 char *parse, *temppos = NULL; 03561 int priority_jump = 0; 03562 AST_DECLARE_APP_ARGS(args, 03563 AST_APP_ARG(queuename); 03564 AST_APP_ARG(interface); 03565 AST_APP_ARG(penalty); 03566 AST_APP_ARG(options); 03567 AST_APP_ARG(membername); 03568 ); 03569 int penalty = 0; 03570 03571 if (ast_strlen_zero(data)) { 03572 ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n"); 03573 return -1; 03574 } 03575 03576 parse = ast_strdupa(data); 03577 03578 AST_STANDARD_APP_ARGS(args, parse); 03579 03580 lu = ast_module_user_add(chan); 03581 03582 if (ast_strlen_zero(args.interface)) { 03583 args.interface = ast_strdupa(chan->name); 03584 temppos = strrchr(args.interface, '-'); 03585 if (temppos) 03586 *temppos = '\0'; 03587 } 03588 03589 if (!ast_strlen_zero(args.penalty)) { 03590 if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) { 03591 ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty); 03592 penalty = 0; 03593 } 03594 } 03595 03596 if (args.options) { 03597 if (strchr(args.options, 'j')) 03598 priority_jump = 1; 03599 } 03600 03601 switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) { 03602 case RES_OKAY: 03603 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", ""); 03604 ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename); 03605 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED"); 03606 res = 0; 03607 break; 03608 case RES_EXISTS: 03609 ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename); 03610 if (priority_jump || ast_opt_priority_jumping) 03611 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 03612 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY"); 03613 res = 0; 03614 break; 03615 case RES_NOSUCHQUEUE: 03616 ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename); 03617 pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE"); 03618 res = 0; 03619 break; 03620 case RES_OUTOFMEMORY: 03621 ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename); 03622 break; 03623 } 03624 03625 ast_module_user_remove(lu); 03626 03627 return res; 03628 }
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 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 2480 of file app_queue.c.
References ast_log(), ast_random(), member::calls, member::lastcall, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.
Referenced by try_calling().
02481 { 02482 if (qe->max_penalty && (mem->penalty > qe->max_penalty)) 02483 return -1; 02484 02485 switch (q->strategy) { 02486 case QUEUE_STRATEGY_RINGALL: 02487 /* Everyone equal, except for penalty */ 02488 tmp->metric = mem->penalty * 1000000; 02489 break; 02490 case QUEUE_STRATEGY_ROUNDROBIN: 02491 if (!pos) { 02492 if (!q->wrapped) { 02493 /* No more channels, start over */ 02494 q->rrpos = 0; 02495 } else { 02496 /* Prioritize next entry */ 02497 q->rrpos++; 02498 } 02499 q->wrapped = 0; 02500 } 02501 /* Fall through */ 02502 case QUEUE_STRATEGY_RRMEMORY: 02503 if (pos < q->rrpos) { 02504 tmp->metric = 1000 + pos; 02505 } else { 02506 if (pos > q->rrpos) 02507 /* Indicate there is another priority */ 02508 q->wrapped = 1; 02509 tmp->metric = pos; 02510 } 02511 tmp->metric += mem->penalty * 1000000; 02512 break; 02513 case QUEUE_STRATEGY_RANDOM: 02514 tmp->metric = ast_random() % 1000; 02515 tmp->metric += mem->penalty * 1000000; 02516 break; 02517 case QUEUE_STRATEGY_FEWESTCALLS: 02518 tmp->metric = mem->calls; 02519 tmp->metric += mem->penalty * 1000000; 02520 break; 02521 case QUEUE_STRATEGY_LEASTRECENT: 02522 if (!mem->lastcall) 02523 tmp->metric = 0; 02524 else 02525 tmp->metric = 1000000 - (time(NULL) - mem->lastcall); 02526 tmp->metric += mem->penalty * 1000000; 02527 break; 02528 default: 02529 ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy); 02530 break; 02531 } 02532 return 0; 02533 }
static void clear_and_free_interfaces | ( | void | ) | [static] |
Definition at line 935 of file app_queue.c.
References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.
Referenced by unload_module().
00936 { 00937 struct member_interface *curint; 00938 00939 AST_LIST_LOCK(&interfaces); 00940 while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list))) 00941 free(curint); 00942 AST_LIST_UNLOCK(&interfaces); 00943 }
static void clear_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 852 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().
00853 { 00854 q->holdtime = 0; 00855 q->callscompleted = 0; 00856 q->callsabandoned = 0; 00857 q->callscompletedinsl = 0; 00858 q->wrapuptime = 0; 00859 }
static int compare_weight | ( | struct call_queue * | rq, | |
struct member * | member | |||
) | [static] |
Definition at line 1701 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().
01702 { 01703 struct call_queue *q; 01704 struct member *mem; 01705 int found = 0; 01706 01707 /* &qlock and &rq->lock already set by try_calling() 01708 * to solve deadlock */ 01709 AST_LIST_TRAVERSE(&queues, q, list) { 01710 if (q == rq) /* don't check myself, could deadlock */ 01711 continue; 01712 ast_mutex_lock(&q->lock); 01713 if (q->count && q->members) { 01714 if ((mem = ao2_find(q->members, member, OBJ_POINTER))) { 01715 ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name); 01716 if (q->weight > rq->weight) { 01717 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); 01718 found = 1; 01719 } 01720 ao2_ref(mem, -1); 01721 } 01722 } 01723 ast_mutex_unlock(&q->lock); 01724 if (found) 01725 break; 01726 } 01727 return found; 01728 }
static char* complete_queue | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4421 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().
04422 { 04423 struct call_queue *q; 04424 char *ret = NULL; 04425 int which = 0; 04426 int wordlen = strlen(word); 04427 04428 AST_LIST_LOCK(&queues); 04429 AST_LIST_TRAVERSE(&queues, q, list) { 04430 if (!strncasecmp(word, q->name, wordlen) && ++which > state) { 04431 ret = ast_strdup(q->name); 04432 break; 04433 } 04434 } 04435 AST_LIST_UNLOCK(&queues); 04436 04437 return ret; 04438 }
static char* complete_queue_add_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4715 of file app_queue.c.
References ast_malloc, ast_strdup, and complete_queue().
04716 { 04717 /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */ 04718 switch (pos) { 04719 case 3: /* Don't attempt to complete name of interface (infinite possibilities) */ 04720 return NULL; 04721 case 4: /* only one possible match, "to" */ 04722 return state == 0 ? ast_strdup("to") : NULL; 04723 case 5: /* <queue> */ 04724 return complete_queue(line, word, pos, state); 04725 case 6: /* only one possible match, "penalty" */ 04726 return state == 0 ? ast_strdup("penalty") : NULL; 04727 case 7: 04728 if (state < 100) { /* 0-99 */ 04729 char *num; 04730 if ((num = ast_malloc(3))) { 04731 sprintf(num, "%d", state); 04732 } 04733 return num; 04734 } else { 04735 return NULL; 04736 } 04737 case 8: /* only one possible match, "as" */ 04738 return state == 0 ? ast_strdup("as") : NULL; 04739 case 9: /* Don't attempt to complete name of member (infinite possibilities) */ 04740 return NULL; 04741 default: 04742 return NULL; 04743 } 04744 }
static char* complete_queue_remove_member | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4781 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(), call_queue::lock, member::membername, and call_queue::members.
04782 { 04783 int which = 0; 04784 struct call_queue *q; 04785 struct member *m; 04786 struct ao2_iterator mem_iter; 04787 04788 /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */ 04789 if (pos > 5 || pos < 3) 04790 return NULL; 04791 if (pos == 4) /* only one possible match, 'from' */ 04792 return state == 0 ? ast_strdup("from") : NULL; 04793 04794 if (pos == 5) /* No need to duplicate code */ 04795 return complete_queue(line, word, pos, state); 04796 04797 /* here is the case for 3, <member> */ 04798 if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */ 04799 AST_LIST_TRAVERSE(&queues, q, list) { 04800 ast_mutex_lock(&q->lock); 04801 mem_iter = ao2_iterator_init(q->members, 0); 04802 while ((m = ao2_iterator_next(&mem_iter))) { 04803 if (++which > state) { 04804 char *tmp; 04805 ast_mutex_unlock(&q->lock); 04806 tmp = m->membername; 04807 ao2_ref(m, -1); 04808 return ast_strdup(tmp); 04809 } 04810 ao2_ref(m, -1); 04811 } 04812 ast_mutex_unlock(&q->lock); 04813 } 04814 } 04815 04816 return NULL; 04817 }
static char* complete_queue_show | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 4440 of file app_queue.c.
References complete_queue().
04441 { 04442 if (pos == 2) 04443 return complete_queue(line, word, pos, state); 04444 return NULL; 04445 }
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 742 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().
00743 { 00744 struct member *cur; 00745 00746 if ((cur = ao2_alloc(sizeof(*cur), NULL))) { 00747 cur->penalty = penalty; 00748 cur->paused = paused; 00749 ast_copy_string(cur->interface, interface, sizeof(cur->interface)); 00750 if (!ast_strlen_zero(membername)) 00751 ast_copy_string(cur->membername, membername, sizeof(cur->membername)); 00752 else 00753 ast_copy_string(cur->membername, interface, sizeof(cur->membername)); 00754 if (!strchr(cur->interface, '/')) 00755 ast_log(LOG_WARNING, "No location at interface '%s'\n", interface); 00756 cur->status = ast_device_state(interface); 00757 } 00758 00759 return cur; 00760 }
static void destroy_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 1158 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().
01159 { 01160 free_members(q, 1); 01161 ast_mutex_destroy(&q->lock); 01162 ao2_ref(q->members, -1); 01163 free(q); 01164 }
static void* device_state_thread | ( | void * | data | ) | [static] |
Consumer of the statechange queue.
Definition at line 690 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().
00691 { 00692 struct statechange *sc = NULL; 00693 00694 while (!device_state.stop) { 00695 ast_mutex_lock(&device_state.lock); 00696 if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) { 00697 ast_cond_wait(&device_state.cond, &device_state.lock); 00698 sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry); 00699 } 00700 ast_mutex_unlock(&device_state.lock); 00701 00702 /* Check to see if we were woken up to see the request to stop */ 00703 if (device_state.stop) 00704 break; 00705 00706 if (!sc) 00707 continue; 00708 00709 handle_statechange(sc); 00710 00711 free(sc); 00712 sc = NULL; 00713 } 00714 00715 if (sc) 00716 free(sc); 00717 00718 while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) 00719 free(sc); 00720 00721 return NULL; 00722 }
static void do_hang | ( | struct callattempt * | o | ) | [static] |
common hangup actions
Definition at line 1731 of file app_queue.c.
References ast_hangup(), callattempt::chan, and callattempt::stillgoing.
Referenced by ring_entry().
01732 { 01733 o->stillgoing = 0; 01734 ast_hangup(o->chan); 01735 o->chan = NULL; 01736 }
static void dump_queue_members | ( | struct call_queue * | pm_queue | ) | [static] |
Definition at line 3094 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().
03095 { 03096 struct member *cur_member; 03097 char value[PM_MAX_LEN]; 03098 int value_len = 0; 03099 int res; 03100 struct ao2_iterator mem_iter; 03101 03102 memset(value, 0, sizeof(value)); 03103 03104 if (!pm_queue) 03105 return; 03106 03107 mem_iter = ao2_iterator_init(pm_queue->members, 0); 03108 while ((cur_member = ao2_iterator_next(&mem_iter))) { 03109 if (!cur_member->dynamic) { 03110 ao2_ref(cur_member, -1); 03111 continue; 03112 } 03113 03114 res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s", 03115 value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername); 03116 03117 ao2_ref(cur_member, -1); 03118 03119 if (res != strlen(value + value_len)) { 03120 ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n"); 03121 break; 03122 } 03123 value_len += res; 03124 } 03125 03126 if (value_len && !cur_member) { 03127 if (ast_db_put(pm_family, pm_queue->name, value)) 03128 ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n"); 03129 } else 03130 /* Delete the entry if the queue is empty or there is an error */ 03131 ast_db_del(pm_family, pm_queue->name); 03132 }
static struct callattempt* find_best | ( | struct callattempt * | outgoing | ) | [static] |
find the entry with the best metric, or NULL
Definition at line 1910 of file app_queue.c.
References callattempt::metric, and callattempt::q_next.
01911 { 01912 struct callattempt *best = NULL, *cur; 01913 01914 for (cur = outgoing; cur; cur = cur->q_next) { 01915 if (cur->stillgoing && /* Not already done */ 01916 !cur->chan && /* Isn't already going */ 01917 (!best || cur->metric < best->metric)) { /* We haven't found one yet, or it's better */ 01918 best = cur; 01919 } 01920 } 01921 01922 return best; 01923 }
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 1169 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(), 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().
01170 { 01171 struct ast_variable *v; 01172 struct call_queue *q; 01173 struct member *m; 01174 struct ao2_iterator mem_iter; 01175 char *interface = NULL; 01176 char *tmp, *tmp_name; 01177 char tmpbuf[64]; /* Must be longer than the longest queue param name. */ 01178 01179 /* Find the queue in the in-core list (we will create a new one if not found). */ 01180 AST_LIST_TRAVERSE(&queues, q, list) { 01181 if (!strcasecmp(q->name, queuename)) 01182 break; 01183 } 01184 01185 /* Static queues override realtime. */ 01186 if (q) { 01187 ast_mutex_lock(&q->lock); 01188 if (!q->realtime) { 01189 if (q->dead) { 01190 ast_mutex_unlock(&q->lock); 01191 return NULL; 01192 } else { 01193 ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name); 01194 ast_mutex_unlock(&q->lock); 01195 return q; 01196 } 01197 } 01198 } else if (!member_config) 01199 /* Not found in the list, and it's not realtime ... */ 01200 return NULL; 01201 01202 /* Check if queue is defined in realtime. */ 01203 if (!queue_vars) { 01204 /* Delete queue from in-core list if it has been deleted in realtime. */ 01205 if (q) { 01206 /*! \note Hmm, can't seem to distinguish a DB failure from a not 01207 found condition... So we might delete an in-core queue 01208 in case of DB failure. */ 01209 ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename); 01210 01211 q->dead = 1; 01212 /* Delete if unused (else will be deleted when last caller leaves). */ 01213 if (!q->count) { 01214 /* Delete. */ 01215 AST_LIST_REMOVE(&queues, q, list); 01216 ast_mutex_unlock(&q->lock); 01217 destroy_queue(q); 01218 } else 01219 ast_mutex_unlock(&q->lock); 01220 } 01221 return NULL; 01222 } 01223 01224 /* Create a new queue if an in-core entry does not exist yet. */ 01225 if (!q) { 01226 if (!(q = alloc_queue(queuename))) 01227 return NULL; 01228 ast_mutex_lock(&q->lock); 01229 clear_queue(q); 01230 q->realtime = 1; 01231 init_queue(q); /* Ensure defaults for all parameters not set explicitly. */ 01232 AST_LIST_INSERT_HEAD(&queues, q, list); 01233 } 01234 01235 memset(tmpbuf, 0, sizeof(tmpbuf)); 01236 for (v = queue_vars; v; v = v->next) { 01237 /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */ 01238 if ((tmp = strchr(v->name, '_'))) { 01239 ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf)); 01240 tmp_name = tmpbuf; 01241 tmp = tmp_name; 01242 while ((tmp = strchr(tmp, '_'))) 01243 *tmp++ = '-'; 01244 } else 01245 tmp_name = v->name; 01246 queue_set_param(q, tmp_name, v->value, -1, 0); 01247 } 01248 01249 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 01250 rr_dep_warning(); 01251 01252 /* Temporarily set realtime members dead so we can detect deleted ones. 01253 * Also set the membercount correctly for realtime*/ 01254 mem_iter = ao2_iterator_init(q->members, 0); 01255 while ((m = ao2_iterator_next(&mem_iter))) { 01256 q->membercount++; 01257 if (m->realtime) 01258 m->dead = 1; 01259 ao2_ref(m, -1); 01260 } 01261 01262 while ((interface = ast_category_browse(member_config, interface))) { 01263 rt_handle_member_record(q, interface, 01264 ast_variable_retrieve(member_config, interface, "membername"), 01265 ast_variable_retrieve(member_config, interface, "penalty"), 01266 ast_variable_retrieve(member_config, interface, "paused")); 01267 } 01268 01269 /* Delete all realtime members that have been deleted in DB. */ 01270 mem_iter = ao2_iterator_init(q->members, 0); 01271 while ((m = ao2_iterator_next(&mem_iter))) { 01272 if (m->dead) { 01273 ao2_unlink(q->members, m); 01274 ast_mutex_unlock(&q->lock); 01275 remove_from_interfaces(m->interface); 01276 ast_mutex_lock(&q->lock); 01277 q->membercount--; 01278 } 01279 ao2_ref(m, -1); 01280 } 01281 01282 ast_mutex_unlock(&q->lock); 01283 01284 return q; 01285 }
static void free_members | ( | struct call_queue * | q, | |
int | all | |||
) | [static] |
Definition at line 1142 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().
01143 { 01144 /* Free non-dynamic members */ 01145 struct member *cur; 01146 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 01147 01148 while ((cur = ao2_iterator_next(&mem_iter))) { 01149 if (all || !cur->dynamic) { 01150 ao2_unlink(q->members, cur); 01151 remove_from_interfaces(cur->interface); 01152 q->membercount--; 01153 } 01154 ao2_ref(cur, -1); 01155 } 01156 }
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 531 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, result, and member::status.
Referenced by join_queue(), queue_exec(), and wait_our_turn().
00532 { 00533 struct member *member; 00534 struct ao2_iterator mem_iter; 00535 enum queue_member_status result = QUEUE_NO_MEMBERS; 00536 00537 ast_mutex_lock(&q->lock); 00538 mem_iter = ao2_iterator_init(q->members, 0); 00539 while ((member = ao2_iterator_next(&mem_iter))) { 00540 if (max_penalty && (member->penalty > max_penalty)) { 00541 ao2_ref(member, -1); 00542 continue; 00543 } 00544 00545 if (member->paused) { 00546 ao2_ref(member, -1); 00547 continue; 00548 } 00549 00550 switch (member->status) { 00551 case AST_DEVICE_INVALID: 00552 /* nothing to do */ 00553 ao2_ref(member, -1); 00554 break; 00555 case AST_DEVICE_UNAVAILABLE: 00556 result = QUEUE_NO_REACHABLE_MEMBERS; 00557 ao2_ref(member, -1); 00558 break; 00559 default: 00560 ast_mutex_unlock(&q->lock); 00561 ao2_ref(member, -1); 00562 return QUEUE_NORMAL; 00563 } 00564 } 00565 00566 ast_mutex_unlock(&q->lock); 00567 return result; 00568 }
static int handle_queue_add_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 4661 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.
04662 { 04663 char *queuename, *interface, *membername = NULL; 04664 int penalty; 04665 04666 if ((argc != 6) && (argc != 8) && (argc != 10)) { 04667 return RESULT_SHOWUSAGE; 04668 } else if (strcmp(argv[4], "to")) { 04669 return RESULT_SHOWUSAGE; 04670 } else if ((argc == 8) && strcmp(argv[6], "penalty")) { 04671 return RESULT_SHOWUSAGE; 04672 } else if ((argc == 10) && strcmp(argv[8], "as")) { 04673 return RESULT_SHOWUSAGE; 04674 } 04675 04676 queuename = argv[5]; 04677 interface = argv[3]; 04678 if (argc >= 8) { 04679 if (sscanf(argv[7], "%d", &penalty) == 1) { 04680 if (penalty < 0) { 04681 ast_cli(fd, "Penalty must be >= 0\n"); 04682 penalty = 0; 04683 } 04684 } else { 04685 ast_cli(fd, "Penalty must be an integer >= 0\n"); 04686 penalty = 0; 04687 } 04688 } else { 04689 penalty = 0; 04690 } 04691 04692 if (argc >= 10) { 04693 membername = argv[9]; 04694 } 04695 04696 switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) { 04697 case RES_OKAY: 04698 ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", ""); 04699 ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename); 04700 return RESULT_SUCCESS; 04701 case RES_EXISTS: 04702 ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename); 04703 return RESULT_FAILURE; 04704 case RES_NOSUCHQUEUE: 04705 ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename); 04706 return RESULT_FAILURE; 04707 case RES_OUTOFMEMORY: 04708 ast_cli(fd, "Out of memory\n"); 04709 return RESULT_FAILURE; 04710 default: 04711 return RESULT_FAILURE; 04712 } 04713 }
static int handle_queue_remove_member | ( | int | fd, | |
int | argc, | |||
char * | argv[] | |||
) | [static] |
Definition at line 4746 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.
04747 { 04748 char *queuename, *interface; 04749 04750 if (argc != 6) { 04751 return RESULT_SHOWUSAGE; 04752 } else if (strcmp(argv[4], "from")) { 04753 return RESULT_SHOWUSAGE; 04754 } 04755 04756 queuename = argv[5]; 04757 interface = argv[3]; 04758 04759 switch (remove_from_queue(queuename, interface)) { 04760 case RES_OKAY: 04761 ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", ""); 04762 ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename); 04763 return RESULT_SUCCESS; 04764 case RES_EXISTS: 04765 ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename); 04766 return RESULT_FAILURE; 04767 case RES_NOSUCHQUEUE: 04768 ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename); 04769 return RESULT_FAILURE; 04770 case RES_OUTOFMEMORY: 04771 ast_cli(fd, "Out of memory\n"); 04772 return RESULT_FAILURE; 04773 case RES_NOT_DYNAMIC: 04774 ast_cli(fd, "Member not dynamic\n"); 04775 return RESULT_FAILURE; 04776 default: 04777 return RESULT_FAILURE; 04778 } 04779 }
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 629 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().
00630 { 00631 struct member_interface *curint; 00632 char *loc; 00633 char *technology; 00634 00635 technology = ast_strdupa(sc->dev); 00636 loc = strchr(technology, '/'); 00637 if (loc) { 00638 *loc++ = '\0'; 00639 } else { 00640 return NULL; 00641 } 00642 00643 AST_LIST_LOCK(&interfaces); 00644 AST_LIST_TRAVERSE(&interfaces, curint, list) { 00645 char *interface; 00646 char *slash_pos; 00647 interface = ast_strdupa(curint->interface); 00648 if ((slash_pos = strchr(interface, '/'))) 00649 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00650 *slash_pos = '\0'; 00651 00652 if (!strcasecmp(interface, sc->dev)) 00653 break; 00654 } 00655 AST_LIST_UNLOCK(&interfaces); 00656 00657 if (!curint) { 00658 if (option_debug > 2) 00659 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)); 00660 return NULL; 00661 } 00662 00663 if (option_debug) 00664 ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state)); 00665 00666 update_status(sc->dev, sc->state); 00667 00668 return NULL; 00669 }
static void hangupcalls | ( | struct callattempt * | outgoing, | |
struct ast_channel * | exception | |||
) | [static] |
Definition at line 1682 of file app_queue.c.
References ao2_ref(), ast_hangup(), callattempt::chan, free, callattempt::member, and callattempt::q_next.
Referenced by try_calling().
01683 { 01684 struct callattempt *oo; 01685 01686 while (outgoing) { 01687 /* Hangup any existing lines we have open */ 01688 if (outgoing->chan && (outgoing->chan != exception)) 01689 ast_hangup(outgoing->chan); 01690 oo = outgoing; 01691 outgoing = outgoing->q_next; 01692 if (oo->member) 01693 ao2_ref(oo->member, -1); 01694 free(oo); 01695 } 01696 }
static void init_queue | ( | struct call_queue * | q | ) | [static] |
Definition at line 801 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::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().
00802 { 00803 int i; 00804 00805 q->dead = 0; 00806 q->retry = DEFAULT_RETRY; 00807 q->timeout = -1; 00808 q->maxlen = 0; 00809 q->announcefrequency = 0; 00810 q->announceholdtime = 0; 00811 q->roundingseconds = 0; /* Default - don't announce seconds */ 00812 q->servicelevel = 0; 00813 q->ringinuse = 1; 00814 q->setinterfacevar = 0; 00815 q->autofill = autofill_default; 00816 q->montype = montype_default; 00817 q->moh[0] = '\0'; 00818 q->announce[0] = '\0'; 00819 q->context[0] = '\0'; 00820 q->monfmt[0] = '\0'; 00821 q->periodicannouncefrequency = 0; 00822 q->reportholdtime = 0; 00823 q->monjoin = 0; 00824 q->wrapuptime = 0; 00825 q->autofill = 0; 00826 q->joinempty = 0; 00827 q->leavewhenempty = 0; 00828 q->memberdelay = 0; 00829 q->maskmemberstatus = 0; 00830 q->eventwhencalled = 0; 00831 q->weight = 0; 00832 q->timeoutrestart = 0; 00833 if (!q->members) 00834 q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn); 00835 q->membercount = 0; 00836 q->found = 1; 00837 ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next)); 00838 ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare)); 00839 ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls)); 00840 ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime)); 00841 ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes)); 00842 ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds)); 00843 ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks)); 00844 ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan)); 00845 ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold)); 00846 ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0])); 00847 for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) { 00848 q->sound_periodicannounce[i][0]='\0'; 00849 } 00850 }
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 500 of file app_queue.c.
References call_queue::head, queue_ent::next, and queue_ent::parent.
Referenced by join_queue().
00501 { 00502 struct queue_ent *cur; 00503 00504 if (!q || !new) 00505 return; 00506 if (prev) { 00507 cur = prev->next; 00508 prev->next = new; 00509 } else { 00510 cur = q->head; 00511 q->head = new; 00512 } 00513 new->next = cur; 00514 new->parent = q; 00515 new->pos = ++(*pos); 00516 new->opos = *pos; 00517 }
static char* int2strat | ( | int | strategy | ) | [static] |
Definition at line 475 of file app_queue.c.
References name, and strategies.
Referenced by __queues_show().
00476 { 00477 int x; 00478 00479 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) { 00480 if (strategy == strategies[x].strategy) 00481 return strategies[x].name; 00482 } 00483 00484 return "<unknown>"; 00485 }
static struct member* interface_exists | ( | struct call_queue * | q, | |
const char * | interface | |||
) | [static] |
Definition at line 3070 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().
03071 { 03072 struct member *mem; 03073 struct ao2_iterator mem_iter; 03074 03075 if (!q) 03076 return NULL; 03077 03078 mem_iter = ao2_iterator_init(q->members, 0); 03079 while ((mem = ao2_iterator_next(&mem_iter))) { 03080 if (!strcasecmp(interface, mem->interface)) 03081 return mem; 03082 ao2_ref(mem, -1); 03083 } 03084 03085 return NULL; 03086 }
static int interface_exists_global | ( | const char * | interface | ) | [static] |
Definition at line 888 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().
00889 { 00890 struct call_queue *q; 00891 struct member *mem, tmpmem; 00892 int ret = 0; 00893 00894 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 00895 00896 AST_LIST_LOCK(&queues); 00897 AST_LIST_TRAVERSE(&queues, q, list) { 00898 ast_mutex_lock(&q->lock); 00899 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 00900 ao2_ref(mem, -1); 00901 ret = 1; 00902 } 00903 ast_mutex_unlock(&q->lock); 00904 if (ret) 00905 break; 00906 } 00907 AST_LIST_UNLOCK(&queues); 00908 00909 return ret; 00910 }
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 2320 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().
02321 { 02322 struct queue_ent *ch; 02323 struct member *cur; 02324 int avl = 0; 02325 int idx = 0; 02326 int res; 02327 02328 if (!qe->parent->autofill) { 02329 /* Atomically read the parent head -- does not need a lock */ 02330 ch = qe->parent->head; 02331 /* If we are now at the top of the head, break out */ 02332 if (ch == qe) { 02333 if (option_debug) 02334 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02335 res = 1; 02336 } else { 02337 if (option_debug) 02338 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02339 res = 0; 02340 } 02341 02342 } else { 02343 /* This needs a lock. How many members are available to be served? */ 02344 ast_mutex_lock(&qe->parent->lock); 02345 02346 ch = qe->parent->head; 02347 02348 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 02349 if (option_debug) 02350 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"); 02351 avl = 1; 02352 } else { 02353 struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0); 02354 while ((cur = ao2_iterator_next(&mem_iter))) { 02355 switch (cur->status) { 02356 case AST_DEVICE_INUSE: 02357 if (!qe->parent->ringinuse) 02358 break; 02359 /* else fall through */ 02360 case AST_DEVICE_NOT_INUSE: 02361 case AST_DEVICE_UNKNOWN: 02362 if (!cur->paused) 02363 avl++; 02364 break; 02365 } 02366 ao2_ref(cur, -1); 02367 } 02368 } 02369 02370 if (option_debug) 02371 ast_log(LOG_DEBUG, "There are %d available members.\n", avl); 02372 02373 while ((idx < avl) && (ch) && (ch != qe)) { 02374 if (!ch->pending) 02375 idx++; 02376 ch = ch->next; 02377 } 02378 02379 /* If the queue entry is within avl [the number of available members] calls from the top ... */ 02380 if (ch && idx < avl) { 02381 if (option_debug) 02382 ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name); 02383 res = 1; 02384 } else { 02385 if (option_debug) 02386 ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name); 02387 res = 0; 02388 } 02389 02390 ast_mutex_unlock(&qe->parent->lock); 02391 } 02392 02393 return res; 02394 }
static int join_queue | ( | char * | queuename, | |
struct queue_ent * | qe, | |||
enum queue_result * | reason | |||
) | [static] |
Definition at line 1403 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().
01404 { 01405 struct call_queue *q; 01406 struct queue_ent *cur, *prev = NULL; 01407 int res = -1; 01408 int pos = 0; 01409 int inserted = 0; 01410 enum queue_member_status stat; 01411 01412 if (!(q = load_realtime_queue(queuename))) 01413 return res; 01414 01415 AST_LIST_LOCK(&queues); 01416 ast_mutex_lock(&q->lock); 01417 01418 /* This is our one */ 01419 stat = get_member_status(q, qe->max_penalty); 01420 if (!q->joinempty && (stat == QUEUE_NO_MEMBERS)) 01421 *reason = QUEUE_JOINEMPTY; 01422 else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) 01423 *reason = QUEUE_JOINUNAVAIL; 01424 else if (q->maxlen && (q->count >= q->maxlen)) 01425 *reason = QUEUE_FULL; 01426 else { 01427 /* There's space for us, put us at the right position inside 01428 * the queue. 01429 * Take into account the priority of the calling user */ 01430 inserted = 0; 01431 prev = NULL; 01432 cur = q->head; 01433 while (cur) { 01434 /* We have higher priority than the current user, enter 01435 * before him, after all the other users with priority 01436 * higher or equal to our priority. */ 01437 if ((!inserted) && (qe->prio > cur->prio)) { 01438 insert_entry(q, prev, qe, &pos); 01439 inserted = 1; 01440 } 01441 cur->pos = ++pos; 01442 prev = cur; 01443 cur = cur->next; 01444 } 01445 /* No luck, join at the end of the queue */ 01446 if (!inserted) 01447 insert_entry(q, prev, qe, &pos); 01448 ast_copy_string(qe->moh, q->moh, sizeof(qe->moh)); 01449 ast_copy_string(qe->announce, q->announce, sizeof(qe->announce)); 01450 ast_copy_string(qe->context, q->context, sizeof(qe->context)); 01451 q->count++; 01452 res = 0; 01453 manager_event(EVENT_FLAG_CALL, "Join", 01454 "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n", 01455 qe->chan->name, 01456 S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */ 01457 S_OR(qe->chan->cid.cid_name, "unknown"), 01458 q->name, qe->pos, q->count, qe->chan->uniqueid ); 01459 if (option_debug) 01460 ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos ); 01461 } 01462 ast_mutex_unlock(&q->lock); 01463 AST_LIST_UNLOCK(&queues); 01464 01465 return res; 01466 }
static void leave_queue | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1638 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().
01639 { 01640 struct call_queue *q; 01641 struct queue_ent *cur, *prev = NULL; 01642 int pos = 0; 01643 01644 if (!(q = qe->parent)) 01645 return; 01646 ast_mutex_lock(&q->lock); 01647 01648 prev = NULL; 01649 for (cur = q->head; cur; cur = cur->next) { 01650 if (cur == qe) { 01651 q->count--; 01652 01653 /* Take us out of the queue */ 01654 manager_event(EVENT_FLAG_CALL, "Leave", 01655 "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n", 01656 qe->chan->name, q->name, q->count, qe->chan->uniqueid); 01657 if (option_debug) 01658 ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name ); 01659 /* Take us out of the queue */ 01660 if (prev) 01661 prev->next = cur->next; 01662 else 01663 q->head = cur->next; 01664 } else { 01665 /* Renumber the people after us in the queue based on a new count */ 01666 cur->pos = ++pos; 01667 prev = cur; 01668 } 01669 } 01670 ast_mutex_unlock(&q->lock); 01671 01672 if (q->dead && !q->count) { 01673 /* It's dead and nobody is in it, so kill it */ 01674 AST_LIST_LOCK(&queues); 01675 AST_LIST_REMOVE(&queues, q, list); 01676 AST_LIST_UNLOCK(&queues); 01677 destroy_queue(q); 01678 } 01679 }
static int load_module | ( | void | ) | [static] |
Definition at line 4901 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().
04902 { 04903 int res; 04904 04905 if (!reload_queues()) 04906 return AST_MODULE_LOAD_DECLINE; 04907 04908 if (queue_persistent_members) 04909 reload_queue_members(); 04910 04911 ast_mutex_init(&device_state.lock); 04912 ast_cond_init(&device_state.cond, NULL); 04913 ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL); 04914 04915 ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 04916 res = ast_register_application(app, queue_exec, synopsis, descrip); 04917 res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip); 04918 res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip); 04919 res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip); 04920 res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip); 04921 res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip); 04922 res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues"); 04923 res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status"); 04924 res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue."); 04925 res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue."); 04926 res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable"); 04927 res |= ast_custom_function_register(&queueagentcount_function); 04928 res |= ast_custom_function_register(&queuemembercount_function); 04929 res |= ast_custom_function_register(&queuememberlist_function); 04930 res |= ast_custom_function_register(&queuewaitingcount_function); 04931 res |= ast_devstate_add(statechange_queue, NULL); 04932 04933 return res; 04934 }
static struct call_queue* load_realtime_queue | ( | const char * | queuename | ) | [static] |
Definition at line 1353 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_qac(), and reload_queue_members().
01354 { 01355 struct ast_variable *queue_vars; 01356 struct ast_config *member_config = NULL; 01357 struct call_queue *q; 01358 01359 /* Find the queue in the in-core list first. */ 01360 AST_LIST_LOCK(&queues); 01361 AST_LIST_TRAVERSE(&queues, q, list) { 01362 if (!strcasecmp(q->name, queuename)) { 01363 break; 01364 } 01365 } 01366 AST_LIST_UNLOCK(&queues); 01367 01368 if (!q || q->realtime) { 01369 /*! \note Load from realtime before taking the global qlock, to avoid blocking all 01370 queue operations while waiting for the DB. 01371 01372 This will be two separate database transactions, so we might 01373 see queue parameters as they were before another process 01374 changed the queue and member list as it was after the change. 01375 Thus we might see an empty member list when a queue is 01376 deleted. In practise, this is unlikely to cause a problem. */ 01377 01378 queue_vars = ast_load_realtime("queues", "name", queuename, NULL); 01379 if (queue_vars) { 01380 member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL); 01381 if (!member_config) { 01382 ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n"); 01383 ast_variables_destroy(queue_vars); 01384 return NULL; 01385 } 01386 } 01387 01388 AST_LIST_LOCK(&queues); 01389 01390 q = find_queue_by_name_rt(queuename, queue_vars, member_config); 01391 if (member_config) 01392 ast_config_destroy(member_config); 01393 if (queue_vars) 01394 ast_variables_destroy(queue_vars); 01395 01396 AST_LIST_UNLOCK(&queues); 01397 } else { 01398 update_realtime_members(q); 01399 } 01400 return q; 01401 }
static int manager_add_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4554 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().
04555 { 04556 const char *queuename, *interface, *penalty_s, *paused_s, *membername; 04557 int paused, penalty = 0; 04558 04559 queuename = astman_get_header(m, "Queue"); 04560 interface = astman_get_header(m, "Interface"); 04561 penalty_s = astman_get_header(m, "Penalty"); 04562 paused_s = astman_get_header(m, "Paused"); 04563 membername = astman_get_header(m, "MemberName"); 04564 04565 if (ast_strlen_zero(queuename)) { 04566 astman_send_error(s, m, "'Queue' not specified."); 04567 return 0; 04568 } 04569 04570 if (ast_strlen_zero(interface)) { 04571 astman_send_error(s, m, "'Interface' not specified."); 04572 return 0; 04573 } 04574 04575 if (ast_strlen_zero(penalty_s)) 04576 penalty = 0; 04577 else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0) 04578 penalty = 0; 04579 04580 if (ast_strlen_zero(paused_s)) 04581 paused = 0; 04582 else 04583 paused = abs(ast_true(paused_s)); 04584 04585 switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) { 04586 case RES_OKAY: 04587 ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", ""); 04588 astman_send_ack(s, m, "Added interface to queue"); 04589 break; 04590 case RES_EXISTS: 04591 astman_send_error(s, m, "Unable to add interface: Already there"); 04592 break; 04593 case RES_NOSUCHQUEUE: 04594 astman_send_error(s, m, "Unable to add interface to queue: No such queue"); 04595 break; 04596 case RES_OUTOFMEMORY: 04597 astman_send_error(s, m, "Out of memory"); 04598 break; 04599 } 04600 04601 return 0; 04602 }
static int manager_pause_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4638 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().
04639 { 04640 const char *queuename, *interface, *paused_s; 04641 int paused; 04642 04643 interface = astman_get_header(m, "Interface"); 04644 paused_s = astman_get_header(m, "Paused"); 04645 queuename = astman_get_header(m, "Queue"); /* Optional - if not supplied, pause the given Interface in all queues */ 04646 04647 if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) { 04648 astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters."); 04649 return 0; 04650 } 04651 04652 paused = abs(ast_true(paused_s)); 04653 04654 if (set_member_paused(queuename, interface, paused)) 04655 astman_send_error(s, m, "Interface not found"); 04656 else 04657 astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully"); 04658 return 0; 04659 }
static int manager_queues_show | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4450 of file app_queue.c.
References __queues_show(), astman_append(), RESULT_SUCCESS, and s.
Referenced by load_module().
04451 { 04452 char *a[] = { "queue", "show" }; 04453 04454 __queues_show(s, 1, -1, 2, a); 04455 astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */ 04456 04457 return RESULT_SUCCESS; 04458 }
static int manager_queues_status | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4461 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, s, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.
Referenced by load_module().
04462 { 04463 time_t now; 04464 int pos; 04465 const char *id = astman_get_header(m,"ActionID"); 04466 const char *queuefilter = astman_get_header(m,"Queue"); 04467 const char *memberfilter = astman_get_header(m,"Member"); 04468 char idText[256] = ""; 04469 struct call_queue *q; 04470 struct queue_ent *qe; 04471 float sl = 0; 04472 struct member *mem; 04473 struct ao2_iterator mem_iter; 04474 04475 astman_send_ack(s, m, "Queue status will follow"); 04476 time(&now); 04477 AST_LIST_LOCK(&queues); 04478 if (!ast_strlen_zero(id)) 04479 snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id); 04480 04481 AST_LIST_TRAVERSE(&queues, q, list) { 04482 ast_mutex_lock(&q->lock); 04483 04484 /* List queue properties */ 04485 if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) { 04486 sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0); 04487 astman_append(s, "Event: QueueParams\r\n" 04488 "Queue: %s\r\n" 04489 "Max: %d\r\n" 04490 "Calls: %d\r\n" 04491 "Holdtime: %d\r\n" 04492 "Completed: %d\r\n" 04493 "Abandoned: %d\r\n" 04494 "ServiceLevel: %d\r\n" 04495 "ServicelevelPerf: %2.1f\r\n" 04496 "Weight: %d\r\n" 04497 "%s" 04498 "\r\n", 04499 q->name, q->maxlen, q->count, q->holdtime, q->callscompleted, 04500 q->callsabandoned, q->servicelevel, sl, q->weight, idText); 04501 /* List Queue Members */ 04502 mem_iter = ao2_iterator_init(q->members, 0); 04503 while ((mem = ao2_iterator_next(&mem_iter))) { 04504 if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) { 04505 astman_append(s, "Event: QueueMember\r\n" 04506 "Queue: %s\r\n" 04507 "Name: %s\r\n" 04508 "Location: %s\r\n" 04509 "Membership: %s\r\n" 04510 "Penalty: %d\r\n" 04511 "CallsTaken: %d\r\n" 04512 "LastCall: %d\r\n" 04513 "Status: %d\r\n" 04514 "Paused: %d\r\n" 04515 "%s" 04516 "\r\n", 04517 q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static", 04518 mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText); 04519 } 04520 ao2_ref(mem, -1); 04521 } 04522 /* List Queue Entries */ 04523 pos = 1; 04524 for (qe = q->head; qe; qe = qe->next) { 04525 astman_append(s, "Event: QueueEntry\r\n" 04526 "Queue: %s\r\n" 04527 "Position: %d\r\n" 04528 "Channel: %s\r\n" 04529 "CallerID: %s\r\n" 04530 "CallerIDName: %s\r\n" 04531 "Wait: %ld\r\n" 04532 "%s" 04533 "\r\n", 04534 q->name, pos++, qe->chan->name, 04535 S_OR(qe->chan->cid.cid_num, "unknown"), 04536 S_OR(qe->chan->cid.cid_name, "unknown"), 04537 (long) (now - qe->start), idText); 04538 } 04539 } 04540 ast_mutex_unlock(&q->lock); 04541 } 04542 04543 astman_append(s, 04544 "Event: QueueStatusComplete\r\n" 04545 "%s" 04546 "\r\n",idText); 04547 04548 AST_LIST_UNLOCK(&queues); 04549 04550 04551 return RESULT_SUCCESS; 04552 }
static int manager_remove_queue_member | ( | struct mansession * | s, | |
const struct message * | m | |||
) | [static] |
Definition at line 4604 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().
04605 { 04606 const char *queuename, *interface; 04607 04608 queuename = astman_get_header(m, "Queue"); 04609 interface = astman_get_header(m, "Interface"); 04610 04611 if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) { 04612 astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters."); 04613 return 0; 04614 } 04615 04616 switch (remove_from_queue(queuename, interface)) { 04617 case RES_OKAY: 04618 ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", ""); 04619 astman_send_ack(s, m, "Removed interface from queue"); 04620 break; 04621 case RES_EXISTS: 04622 astman_send_error(s, m, "Unable to remove interface: Not there"); 04623 break; 04624 case RES_NOSUCHQUEUE: 04625 astman_send_error(s, m, "Unable to remove interface from queue: No such queue"); 04626 break; 04627 case RES_OUTOFMEMORY: 04628 astman_send_error(s, m, "Out of memory"); 04629 break; 04630 case RES_NOT_DYNAMIC: 04631 astman_send_error(s, m, "Member not dynamic"); 04632 break; 04633 } 04634 04635 return 0; 04636 }
static int member_cmp_fn | ( | void * | obj1, | |
void * | obj2, | |||
int | flags | |||
) | [static] |
Definition at line 795 of file app_queue.c.
References member::interface.
Referenced by init_queue().
00796 { 00797 struct member *mem1 = obj1, *mem2 = obj2; 00798 return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH; 00799 }
static int member_hash_fn | ( | const void * | obj, | |
const int | flags | |||
) | [static] |
Definition at line 783 of file app_queue.c.
References compress_char(), and member::interface.
Referenced by init_queue().
00784 { 00785 const struct member *mem = obj; 00786 const char *chname = strchr(mem->interface, '/'); 00787 int ret = 0, i; 00788 if (!chname) 00789 chname = mem->interface; 00790 for (i = 0; i < 5 && chname[i]; i++) 00791 ret += compress_char(chname[i]) << (i * 6); 00792 return ret; 00793 }
static void monjoin_dep_warning | ( | void | ) | [static] |
Definition at line 454 of file app_queue.c.
References ast_log(), and LOG_NOTICE.
Referenced by queue_set_param().
00455 { 00456 static unsigned int warned = 0; 00457 if (!warned) { 00458 ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n"); 00459 warned = 1; 00460 } 00461 }
static int play_file | ( | struct ast_channel * | chan, | |
char * | filename | |||
) | [static] |
Definition at line 1468 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().
01469 { 01470 int res; 01471 01472 ast_stopstream(chan); 01473 01474 res = ast_streamfile(chan, filename, chan->language); 01475 if (!res) 01476 res = ast_waitstream(chan, AST_DIGIT_ANY); 01477 01478 ast_stopstream(chan); 01479 01480 return res; 01481 }
static int pqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3383 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, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
03384 { 03385 struct ast_module_user *lu; 03386 char *parse; 03387 int priority_jump = 0; 03388 AST_DECLARE_APP_ARGS(args, 03389 AST_APP_ARG(queuename); 03390 AST_APP_ARG(interface); 03391 AST_APP_ARG(options); 03392 ); 03393 03394 if (ast_strlen_zero(data)) { 03395 ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03396 return -1; 03397 } 03398 03399 parse = ast_strdupa(data); 03400 03401 AST_STANDARD_APP_ARGS(args, parse); 03402 03403 lu = ast_module_user_add(chan); 03404 03405 if (args.options) { 03406 if (strchr(args.options, 'j')) 03407 priority_jump = 1; 03408 } 03409 03410 if (ast_strlen_zero(args.interface)) { 03411 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03412 ast_module_user_remove(lu); 03413 return -1; 03414 } 03415 03416 if (set_member_paused(args.queuename, args.interface, 1)) { 03417 ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface); 03418 if (priority_jump || ast_opt_priority_jumping) { 03419 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03420 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 03421 ast_module_user_remove(lu); 03422 return 0; 03423 } 03424 } 03425 ast_module_user_remove(lu); 03426 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND"); 03427 return 0; 03428 } 03429 03430 ast_module_user_remove(lu); 03431 pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED"); 03432 03433 return 0; 03434 }
static int ql_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3630 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().
03631 { 03632 struct ast_module_user *u; 03633 char *parse; 03634 03635 AST_DECLARE_APP_ARGS(args, 03636 AST_APP_ARG(queuename); 03637 AST_APP_ARG(uniqueid); 03638 AST_APP_ARG(membername); 03639 AST_APP_ARG(event); 03640 AST_APP_ARG(params); 03641 ); 03642 03643 if (ast_strlen_zero(data)) { 03644 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n"); 03645 return -1; 03646 } 03647 03648 u = ast_module_user_add(chan); 03649 03650 parse = ast_strdupa(data); 03651 03652 AST_STANDARD_APP_ARGS(args, parse); 03653 03654 if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid) 03655 || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) { 03656 ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n"); 03657 ast_module_user_remove(u); 03658 return -1; 03659 } 03660 03661 ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 03662 "%s", args.params ? args.params : ""); 03663 03664 ast_module_user_remove(u); 03665 03666 return 0; 03667 }
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 3681 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().
03682 { 03683 int res=-1; 03684 int ringing=0; 03685 struct ast_module_user *lu; 03686 const char *user_priority; 03687 const char *max_penalty_str; 03688 int prio; 03689 int max_penalty; 03690 enum queue_result reason = QUEUE_UNKNOWN; 03691 /* whether to exit Queue application after the timeout hits */ 03692 int tries = 0; 03693 int noption = 0; 03694 char *parse; 03695 AST_DECLARE_APP_ARGS(args, 03696 AST_APP_ARG(queuename); 03697 AST_APP_ARG(options); 03698 AST_APP_ARG(url); 03699 AST_APP_ARG(announceoverride); 03700 AST_APP_ARG(queuetimeoutstr); 03701 AST_APP_ARG(agi); 03702 ); 03703 /* Our queue entry */ 03704 struct queue_ent qe; 03705 03706 if (ast_strlen_zero(data)) { 03707 ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n"); 03708 return -1; 03709 } 03710 03711 parse = ast_strdupa(data); 03712 AST_STANDARD_APP_ARGS(args, parse); 03713 03714 lu = ast_module_user_add(chan); 03715 03716 /* Setup our queue entry */ 03717 memset(&qe, 0, sizeof(qe)); 03718 qe.start = time(NULL); 03719 03720 /* set the expire time based on the supplied timeout; */ 03721 if (args.queuetimeoutstr) 03722 qe.expire = qe.start + atoi(args.queuetimeoutstr); 03723 else 03724 qe.expire = 0; 03725 03726 /* Get the priority from the variable ${QUEUE_PRIO} */ 03727 user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO"); 03728 if (user_priority) { 03729 if (sscanf(user_priority, "%d", &prio) == 1) { 03730 if (option_debug) 03731 ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n", 03732 chan->name, prio); 03733 } else { 03734 ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n", 03735 user_priority, chan->name); 03736 prio = 0; 03737 } 03738 } else { 03739 if (option_debug > 2) 03740 ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n"); 03741 prio = 0; 03742 } 03743 03744 /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */ 03745 if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) { 03746 if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) { 03747 if (option_debug) 03748 ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n", 03749 chan->name, max_penalty); 03750 } else { 03751 ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n", 03752 max_penalty_str, chan->name); 03753 max_penalty = 0; 03754 } 03755 } else { 03756 max_penalty = 0; 03757 } 03758 03759 if (args.options && (strchr(args.options, 'r'))) 03760 ringing = 1; 03761 03762 if (option_debug) 03763 ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n", 03764 args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio); 03765 03766 qe.chan = chan; 03767 qe.prio = prio; 03768 qe.max_penalty = max_penalty; 03769 qe.last_pos_said = 0; 03770 qe.last_pos = 0; 03771 qe.last_periodic_announce_time = time(NULL); 03772 qe.last_periodic_announce_sound = 0; 03773 qe.valid_digits = 0; 03774 if (!join_queue(args.queuename, &qe, &reason)) { 03775 int makeannouncement = 0; 03776 03777 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""), 03778 S_OR(chan->cid.cid_num, "")); 03779 check_turns: 03780 if (ringing) { 03781 ast_indicate(chan, AST_CONTROL_RINGING); 03782 } else { 03783 ast_moh_start(chan, qe.moh, NULL); 03784 } 03785 03786 /* This is the wait loop for callers 2 through maxlen */ 03787 res = wait_our_turn(&qe, ringing, &reason); 03788 if (res) 03789 goto stop; 03790 03791 for (;;) { 03792 /* This is the wait loop for the head caller*/ 03793 /* To exit, they may get their call answered; */ 03794 /* they may dial a digit from the queue context; */ 03795 /* or, they may timeout. */ 03796 03797 enum queue_member_status stat; 03798 03799 /* Leave if we have exceeded our queuetimeout */ 03800 if (qe.expire && (time(NULL) > qe.expire)) { 03801 record_abandoned(&qe); 03802 reason = QUEUE_TIMEOUT; 03803 res = 0; 03804 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 03805 break; 03806 } 03807 03808 if (makeannouncement) { 03809 /* Make a position announcement, if enabled */ 03810 if (qe.parent->announcefrequency && !ringing) 03811 if ((res = say_position(&qe))) 03812 goto stop; 03813 03814 } 03815 makeannouncement = 1; 03816 03817 /* Make a periodic announcement, if enabled */ 03818 if (qe.parent->periodicannouncefrequency && !ringing) 03819 if ((res = say_periodic_announcement(&qe))) 03820 goto stop; 03821 03822 /* Try calling all queue members for 'timeout' seconds */ 03823 res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi); 03824 if (res) 03825 goto stop; 03826 03827 stat = get_member_status(qe.parent, qe.max_penalty); 03828 03829 /* exit after 'timeout' cycle if 'n' option enabled */ 03830 if (noption && tries >= qe.parent->membercount) { 03831 if (option_verbose > 2) 03832 ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n"); 03833 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 03834 record_abandoned(&qe); 03835 reason = QUEUE_TIMEOUT; 03836 res = 0; 03837 break; 03838 } 03839 03840 /* leave the queue if no agents, if enabled */ 03841 if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 03842 record_abandoned(&qe); 03843 reason = QUEUE_LEAVEEMPTY; 03844 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 03845 res = 0; 03846 break; 03847 } 03848 03849 /* leave the queue if no reachable agents, if enabled */ 03850 if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 03851 record_abandoned(&qe); 03852 reason = QUEUE_LEAVEUNAVAIL; 03853 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start)); 03854 res = 0; 03855 break; 03856 } 03857 03858 /* Leave if we have exceeded our queuetimeout */ 03859 if (qe.expire && (time(NULL) > qe.expire)) { 03860 record_abandoned(&qe); 03861 reason = QUEUE_TIMEOUT; 03862 res = 0; 03863 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos); 03864 break; 03865 } 03866 03867 /* If using dynamic realtime members, we should regenerate the member list for this queue */ 03868 update_realtime_members(qe.parent); 03869 03870 /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */ 03871 res = wait_a_bit(&qe); 03872 if (res) 03873 goto stop; 03874 03875 03876 /* Since this is a priority queue and 03877 * it is not sure that we are still at the head 03878 * of the queue, go and check for our turn again. 03879 */ 03880 if (!is_our_turn(&qe)) { 03881 if (option_debug) 03882 ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n", 03883 qe.chan->name); 03884 goto check_turns; 03885 } 03886 } 03887 03888 stop: 03889 if (res) { 03890 if (res < 0) { 03891 if (!qe.handled) { 03892 record_abandoned(&qe); 03893 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON", 03894 "%d|%d|%ld", qe.pos, qe.opos, 03895 (long) time(NULL) - qe.start); 03896 } 03897 res = -1; 03898 } else if (qe.valid_digits) { 03899 ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY", 03900 "%s|%d", qe.digits, qe.pos); 03901 } 03902 } 03903 03904 /* Don't allow return code > 0 */ 03905 if (res >= 0 && res != AST_PBX_KEEPALIVE) { 03906 res = 0; 03907 if (ringing) { 03908 ast_indicate(chan, -1); 03909 } else { 03910 ast_moh_stop(chan); 03911 } 03912 ast_stopstream(chan); 03913 } 03914 leave_queue(&qe); 03915 if (reason != QUEUE_UNKNOWN) 03916 set_queue_result(chan, reason); 03917 } else { 03918 ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename); 03919 set_queue_result(chan, reason); 03920 res = 0; 03921 } 03922 ast_module_user_remove(lu); 03923 03924 return res; 03925 }
static int queue_function_qac | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 3927 of file app_queue.c.
References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), load_realtime_queue(), call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::members, and member::status.
03928 { 03929 int count = 0; 03930 struct call_queue *q; 03931 struct ast_module_user *lu; 03932 struct member *m; 03933 struct ao2_iterator mem_iter; 03934 03935 buf[0] = '\0'; 03936 03937 if (ast_strlen_zero(data)) { 03938 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 03939 return -1; 03940 } 03941 03942 lu = ast_module_user_add(chan); 03943 03944 if ((q = load_realtime_queue(data))) { 03945 ast_mutex_lock(&q->lock); 03946 mem_iter = ao2_iterator_init(q->members, 0); 03947 while ((m = ao2_iterator_next(&mem_iter))) { 03948 /* Count the agents who are logged in and presently answering calls */ 03949 if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) { 03950 count++; 03951 } 03952 ao2_ref(m, -1); 03953 } 03954 ast_mutex_unlock(&q->lock); 03955 } else 03956 ast_log(LOG_WARNING, "queue %s was not found\n", data); 03957 03958 snprintf(buf, len, "%d", count); 03959 ast_module_user_remove(lu); 03960 03961 return 0; 03962 }
static int queue_function_queuememberlist | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 4007 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.
04008 { 04009 struct ast_module_user *u; 04010 struct call_queue *q; 04011 struct member *m; 04012 04013 /* Ensure an otherwise empty list doesn't return garbage */ 04014 buf[0] = '\0'; 04015 04016 if (ast_strlen_zero(data)) { 04017 ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n"); 04018 return -1; 04019 } 04020 04021 u = ast_module_user_add(chan); 04022 04023 AST_LIST_LOCK(&queues); 04024 AST_LIST_TRAVERSE(&queues, q, list) { 04025 if (!strcasecmp(q->name, data)) { 04026 ast_mutex_lock(&q->lock); 04027 break; 04028 } 04029 } 04030 AST_LIST_UNLOCK(&queues); 04031 04032 if (q) { 04033 int buflen = 0, count = 0; 04034 struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0); 04035 04036 while ((m = ao2_iterator_next(&mem_iter))) { 04037 /* strcat() is always faster than printf() */ 04038 if (count++) { 04039 strncat(buf + buflen, ",", len - buflen - 1); 04040 buflen++; 04041 } 04042 strncat(buf + buflen, m->membername, len - buflen - 1); 04043 buflen += strlen(m->membername); 04044 /* Safeguard against overflow (negative length) */ 04045 if (buflen >= len - 2) { 04046 ao2_ref(m, -1); 04047 ast_log(LOG_WARNING, "Truncating list\n"); 04048 break; 04049 } 04050 ao2_ref(m, -1); 04051 } 04052 ast_mutex_unlock(&q->lock); 04053 } else 04054 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04055 04056 /* We should already be terminated, but let's make sure. */ 04057 buf[len - 1] = '\0'; 04058 ast_module_user_remove(u); 04059 04060 return 0; 04061 }
static int queue_function_queuewaitingcount | ( | struct ast_channel * | chan, | |
char * | cmd, | |||
char * | data, | |||
char * | buf, | |||
size_t | len | |||
) | [static] |
Definition at line 3964 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.
03965 { 03966 int count = 0; 03967 struct call_queue *q; 03968 struct ast_module_user *lu; 03969 struct ast_variable *var = NULL; 03970 03971 buf[0] = '\0'; 03972 03973 if (ast_strlen_zero(data)) { 03974 ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd); 03975 return -1; 03976 } 03977 03978 lu = ast_module_user_add(chan); 03979 03980 AST_LIST_LOCK(&queues); 03981 AST_LIST_TRAVERSE(&queues, q, list) { 03982 if (!strcasecmp(q->name, data)) { 03983 ast_mutex_lock(&q->lock); 03984 break; 03985 } 03986 } 03987 AST_LIST_UNLOCK(&queues); 03988 03989 if (q) { 03990 count = q->count; 03991 ast_mutex_unlock(&q->lock); 03992 } else if ((var = ast_load_realtime("queues", "name", data, NULL))) { 03993 /* if the queue is realtime but was not found in memory, this 03994 * means that the queue had been deleted from memory since it was 03995 * "dead." This means it has a 0 waiting count 03996 */ 03997 count = 0; 03998 ast_variables_destroy(var); 03999 } else 04000 ast_log(LOG_WARNING, "queue %s was not found\n", data); 04001 04002 snprintf(buf, len, "%d", count); 04003 ast_module_user_remove(lu); 04004 return 0; 04005 }
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 952 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::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().
00953 { 00954 if (!strcasecmp(param, "musicclass") || 00955 !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) { 00956 ast_copy_string(q->moh, val, sizeof(q->moh)); 00957 } else if (!strcasecmp(param, "announce")) { 00958 ast_copy_string(q->announce, val, sizeof(q->announce)); 00959 } else if (!strcasecmp(param, "context")) { 00960 ast_copy_string(q->context, val, sizeof(q->context)); 00961 } else if (!strcasecmp(param, "timeout")) { 00962 q->timeout = atoi(val); 00963 if (q->timeout < 0) 00964 q->timeout = DEFAULT_TIMEOUT; 00965 } else if (!strcasecmp(param, "ringinuse")) { 00966 q->ringinuse = ast_true(val); 00967 } else if (!strcasecmp(param, "setinterfacevar")) { 00968 q->setinterfacevar = ast_true(val); 00969 } else if (!strcasecmp(param, "monitor-join")) { 00970 monjoin_dep_warning(); 00971 q->monjoin = ast_true(val); 00972 } else if (!strcasecmp(param, "monitor-format")) { 00973 ast_copy_string(q->monfmt, val, sizeof(q->monfmt)); 00974 } else if (!strcasecmp(param, "queue-youarenext")) { 00975 ast_copy_string(q->sound_next, val, sizeof(q->sound_next)); 00976 } else if (!strcasecmp(param, "queue-thereare")) { 00977 ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare)); 00978 } else if (!strcasecmp(param, "queue-callswaiting")) { 00979 ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls)); 00980 } else if (!strcasecmp(param, "queue-holdtime")) { 00981 ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime)); 00982 } else if (!strcasecmp(param, "queue-minutes")) { 00983 ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes)); 00984 } else if (!strcasecmp(param, "queue-seconds")) { 00985 ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds)); 00986 } else if (!strcasecmp(param, "queue-lessthan")) { 00987 ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan)); 00988 } else if (!strcasecmp(param, "queue-thankyou")) { 00989 ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks)); 00990 } else if (!strcasecmp(param, "queue-reporthold")) { 00991 ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold)); 00992 } else if (!strcasecmp(param, "announce-frequency")) { 00993 q->announcefrequency = atoi(val); 00994 } else if (!strcasecmp(param, "announce-round-seconds")) { 00995 q->roundingseconds = atoi(val); 00996 if (q->roundingseconds>60 || q->roundingseconds<0) { 00997 if (linenum >= 0) { 00998 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 00999 "using 0 instead for queue '%s' at line %d of queues.conf\n", 01000 val, param, q->name, linenum); 01001 } else { 01002 ast_log(LOG_WARNING, "'%s' isn't a valid value for %s " 01003 "using 0 instead for queue '%s'\n", val, param, q->name); 01004 } 01005 q->roundingseconds=0; 01006 } 01007 } else if (!strcasecmp(param, "announce-holdtime")) { 01008 if (!strcasecmp(val, "once")) 01009 q->announceholdtime = ANNOUNCEHOLDTIME_ONCE; 01010 else if (ast_true(val)) 01011 q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS; 01012 else 01013 q->announceholdtime = 0; 01014 } else if (!strcasecmp(param, "periodic-announce")) { 01015 if (strchr(val, '|')) { 01016 char *s, *buf = ast_strdupa(val); 01017 unsigned int i = 0; 01018 01019 while ((s = strsep(&buf, "|"))) { 01020 ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i])); 01021 i++; 01022 if (i == MAX_PERIODIC_ANNOUNCEMENTS) 01023 break; 01024 } 01025 } else { 01026 ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0])); 01027 } 01028 } else if (!strcasecmp(param, "periodic-announce-frequency")) { 01029 q->periodicannouncefrequency = atoi(val); 01030 } else if (!strcasecmp(param, "retry")) { 01031 q->retry = atoi(val); 01032 if (q->retry <= 0) 01033 q->retry = DEFAULT_RETRY; 01034 } else if (!strcasecmp(param, "wrapuptime")) { 01035 q->wrapuptime = atoi(val); 01036 } else if (!strcasecmp(param, "autofill")) { 01037 q->autofill = ast_true(val); 01038 } else if (!strcasecmp(param, "monitor-type")) { 01039 if (!strcasecmp(val, "mixmonitor")) 01040 q->montype = 1; 01041 } else if (!strcasecmp(param, "autopause")) { 01042 q->autopause = ast_true(val); 01043 } else if (!strcasecmp(param, "maxlen")) { 01044 q->maxlen = atoi(val); 01045 if (q->maxlen < 0) 01046 q->maxlen = 0; 01047 } else if (!strcasecmp(param, "servicelevel")) { 01048 q->servicelevel= atoi(val); 01049 } else if (!strcasecmp(param, "strategy")) { 01050 q->strategy = strat2int(val); 01051 if (q->strategy < 0) { 01052 ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", 01053 val, q->name); 01054 q->strategy = QUEUE_STRATEGY_RINGALL; 01055 } 01056 } else if (!strcasecmp(param, "joinempty")) { 01057 if (!strcasecmp(val, "strict")) 01058 q->joinempty = QUEUE_EMPTY_STRICT; 01059 else if (ast_true(val)) 01060 q->joinempty = QUEUE_EMPTY_NORMAL; 01061 else 01062 q->joinempty = 0; 01063 } else if (!strcasecmp(param, "leavewhenempty")) { 01064 if (!strcasecmp(val, "strict")) 01065 q->leavewhenempty = QUEUE_EMPTY_STRICT; 01066 else if (ast_true(val)) 01067 q->leavewhenempty = QUEUE_EMPTY_NORMAL; 01068 else 01069 q->leavewhenempty = 0; 01070 } else if (!strcasecmp(param, "eventmemberstatus")) { 01071 q->maskmemberstatus = !ast_true(val); 01072 } else if (!strcasecmp(param, "eventwhencalled")) { 01073 if (!strcasecmp(val, "vars")) { 01074 q->eventwhencalled = QUEUE_EVENT_VARIABLES; 01075 } else { 01076 q->eventwhencalled = ast_true(val) ? 1 : 0; 01077 } 01078 } else if (!strcasecmp(param, "reportholdtime")) { 01079 q->reportholdtime = ast_true(val); 01080 } else if (!strcasecmp(param, "memberdelay")) { 01081 q->memberdelay = atoi(val); 01082 } else if (!strcasecmp(param, "weight")) { 01083 q->weight = atoi(val); 01084 if (q->weight) 01085 use_weight++; 01086 /* With Realtime queues, if the last queue using weights is deleted in realtime, 01087 we will not see any effect on use_weight until next reload. */ 01088 } else if (!strcasecmp(param, "timeoutrestart")) { 01089 q->timeoutrestart = ast_true(val); 01090 } else if (failunknown) { 01091 if (linenum >= 0) { 01092 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n", 01093 q->name, param, linenum); 01094 } else { 01095 ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param); 01096 } 01097 } 01098 }
static int queue_show | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 4416 of file app_queue.c.
References __queues_show().
Referenced by __queues_show().
04417 { 04418 return __queues_show(NULL, 0, fd, argc, argv); 04419 }
static void recalc_holdtime | ( | struct queue_ent * | qe, | |
int | newholdtime | |||
) | [static] |
Definition at line 1623 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().
01624 { 01625 int oldvalue; 01626 01627 /* Calculate holdtime using a recursive boxcar filter */ 01628 /* Thanks to SRT for this contribution */ 01629 /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */ 01630 01631 ast_mutex_lock(&qe->parent->lock); 01632 oldvalue = qe->parent->holdtime; 01633 qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2; 01634 ast_mutex_unlock(&qe->parent->lock); 01635 }
static void record_abandoned | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 2031 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().
02032 { 02033 ast_mutex_lock(&qe->parent->lock); 02034 manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon", 02035 "Queue: %s\r\n" 02036 "Uniqueid: %s\r\n" 02037 "Position: %d\r\n" 02038 "OriginalPosition: %d\r\n" 02039 "HoldTime: %d\r\n", 02040 qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start)); 02041 02042 qe->parent->callsabandoned++; 02043 ast_mutex_unlock(&qe->parent->lock); 02044 }
static int reload | ( | void | ) | [static] |
Definition at line 4936 of file app_queue.c.
References reload_queues().
04937 { 04938 reload_queues(); 04939 return 0; 04940 }
static void reload_queue_members | ( | void | ) | [static] |
Definition at line 3288 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().
03289 { 03290 char *cur_ptr; 03291 char *queue_name; 03292 char *member; 03293 char *interface; 03294 char *membername = NULL; 03295 char *penalty_tok; 03296 int penalty = 0; 03297 char *paused_tok; 03298 int paused = 0; 03299 struct ast_db_entry *db_tree; 03300 struct ast_db_entry *entry; 03301 struct call_queue *cur_queue; 03302 char queue_data[PM_MAX_LEN]; 03303 03304 AST_LIST_LOCK(&queues); 03305 03306 /* Each key in 'pm_family' is the name of a queue */ 03307 db_tree = ast_db_gettree(pm_family, NULL); 03308 for (entry = db_tree; entry; entry = entry->next) { 03309 03310 queue_name = entry->key + strlen(pm_family) + 2; 03311 03312 AST_LIST_TRAVERSE(&queues, cur_queue, list) { 03313 ast_mutex_lock(&cur_queue->lock); 03314 if (!strcmp(queue_name, cur_queue->name)) 03315 break; 03316 ast_mutex_unlock(&cur_queue->lock); 03317 } 03318 03319 if (!cur_queue) 03320 cur_queue = load_realtime_queue(queue_name); 03321 03322 if (!cur_queue) { 03323 /* If the queue no longer exists, remove it from the 03324 * database */ 03325 ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name); 03326 ast_db_del(pm_family, queue_name); 03327 continue; 03328 } else 03329 ast_mutex_unlock(&cur_queue->lock); 03330 03331 if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN)) 03332 continue; 03333 03334 cur_ptr = queue_data; 03335 while ((member = strsep(&cur_ptr, "|"))) { 03336 if (ast_strlen_zero(member)) 03337 continue; 03338 03339 interface = strsep(&member, ";"); 03340 penalty_tok = strsep(&member, ";"); 03341 paused_tok = strsep(&member, ";"); 03342 membername = strsep(&member, ";"); 03343 03344 if (!penalty_tok) { 03345 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name); 03346 break; 03347 } 03348 penalty = strtol(penalty_tok, NULL, 10); 03349 if (errno == ERANGE) { 03350 ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok); 03351 break; 03352 } 03353 03354 if (!paused_tok) { 03355 ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name); 03356 break; 03357 } 03358 paused = strtol(paused_tok, NULL, 10); 03359 if ((errno == ERANGE) || paused < 0 || paused > 1) { 03360 ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok); 03361 break; 03362 } 03363 if (ast_strlen_zero(membername)) 03364 membername = interface; 03365 03366 if (option_debug) 03367 ast_log(LOG_DEBUG, "Reload Members: Queue: %s Member: %s Name: %s Penalty: %d Paused: %d\n", queue_name, interface, membername, penalty, paused); 03368 03369 if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) { 03370 ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n"); 03371 break; 03372 } 03373 } 03374 } 03375 03376 AST_LIST_UNLOCK(&queues); 03377 if (db_tree) { 03378 ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n"); 03379 ast_db_freetree(db_tree); 03380 } 03381 }
static int reload_queues | ( | void | ) | [static] |
Definition at line 4100 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().
04101 { 04102 struct call_queue *q; 04103 struct ast_config *cfg; 04104 char *cat, *tmp; 04105 struct ast_variable *var; 04106 struct member *cur, *newm; 04107 struct ao2_iterator mem_iter; 04108 int new; 04109 const char *general_val = NULL; 04110 char parse[80]; 04111 char *interface; 04112 char *membername = NULL; 04113 int penalty; 04114 AST_DECLARE_APP_ARGS(args, 04115 AST_APP_ARG(interface); 04116 AST_APP_ARG(penalty); 04117 AST_APP_ARG(membername); 04118 ); 04119 04120 if (!(cfg = ast_config_load("queues.conf"))) { 04121 ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n"); 04122 return 0; 04123 } 04124 AST_LIST_LOCK(&queues); 04125 use_weight=0; 04126 /* Mark all non-realtime queues as dead for the moment */ 04127 AST_LIST_TRAVERSE(&queues, q, list) { 04128 if (!q->realtime) { 04129 q->dead = 1; 04130 q->found = 0; 04131 } 04132 } 04133 04134 /* Chug through config file */ 04135 cat = NULL; 04136 while ((cat = ast_category_browse(cfg, cat)) ) { 04137 if (!strcasecmp(cat, "general")) { 04138 /* Initialize global settings */ 04139 queue_persistent_members = 0; 04140 if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers"))) 04141 queue_persistent_members = ast_true(general_val); 04142 autofill_default = 0; 04143 if ((general_val = ast_variable_retrieve(cfg, "general", "autofill"))) 04144 autofill_default = ast_true(general_val); 04145 montype_default = 0; 04146 if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type"))) 04147 if (!strcasecmp(general_val, "mixmonitor")) 04148 montype_default = 1; 04149 } else { /* Define queue */ 04150 /* Look for an existing one */ 04151 AST_LIST_TRAVERSE(&queues, q, list) { 04152 if (!strcmp(q->name, cat)) 04153 break; 04154 } 04155 if (!q) { 04156 /* Make one then */ 04157 if (!(q = alloc_queue(cat))) { 04158 /* TODO: Handle memory allocation failure */ 04159 } 04160 new = 1; 04161 } else 04162 new = 0; 04163 if (q) { 04164 if (!new) 04165 ast_mutex_lock(&q->lock); 04166 /* Check if a queue with this name already exists */ 04167 if (q->found) { 04168 ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat); 04169 if (!new) 04170 ast_mutex_unlock(&q->lock); 04171 continue; 04172 } 04173 /* Re-initialize the queue, and clear statistics */ 04174 init_queue(q); 04175 clear_queue(q); 04176 mem_iter = ao2_iterator_init(q->members, 0); 04177 while ((cur = ao2_iterator_next(&mem_iter))) { 04178 if (!cur->dynamic) { 04179 cur->delme = 1; 04180 } 04181 ao2_ref(cur, -1); 04182 } 04183 for (var = ast_variable_browse(cfg, cat); var; var = var->next) { 04184 if (!strcasecmp(var->name, "member")) { 04185 struct member tmpmem; 04186 membername = NULL; 04187 04188 /* Add a new member */ 04189 ast_copy_string(parse, var->value, sizeof(parse)); 04190 04191 AST_NONSTANDARD_APP_ARGS(args, parse, ','); 04192 04193 interface = args.interface; 04194 if (!ast_strlen_zero(args.penalty)) { 04195 tmp = args.penalty; 04196 while (*tmp && *tmp < 33) tmp++; 04197 penalty = atoi(tmp); 04198 if (penalty < 0) { 04199 penalty = 0; 04200 } 04201 } else 04202 penalty = 0; 04203 04204 if (!ast_strlen_zero(args.membername)) { 04205 membername = args.membername; 04206 while (*membername && *membername < 33) membername++; 04207 } 04208 04209 /* Find the old position in the list */ 04210 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 04211 cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK); 04212 04213 newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0); 04214 ao2_link(q->members, newm); 04215 ao2_ref(newm, -1); 04216 newm = NULL; 04217 04218 if (cur) 04219 ao2_ref(cur, -1); 04220 else { 04221 /* Add them to the master int list if necessary */ 04222 add_to_interfaces(interface); 04223 q->membercount++; 04224 } 04225 } else { 04226 queue_set_param(q, var->name, var->value, var->lineno, 1); 04227 } 04228 } 04229 04230 /* Free remaining members marked as delme */ 04231 mem_iter = ao2_iterator_init(q->members, 0); 04232 while ((cur = ao2_iterator_next(&mem_iter))) { 04233 if (! cur->delme) { 04234 ao2_ref(cur, -1); 04235 continue; 04236 } 04237 04238 q->membercount--; 04239 ao2_unlink(q->members, cur); 04240 remove_from_interfaces(cur->interface); 04241 ao2_ref(cur, -1); 04242 } 04243 04244 if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN) 04245 rr_dep_warning(); 04246 04247 if (new) { 04248 AST_LIST_INSERT_HEAD(&queues, q, list); 04249 } else 04250 ast_mutex_unlock(&q->lock); 04251 } 04252 } 04253 } 04254 ast_config_destroy(cfg); 04255 AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) { 04256 if (q->dead) { 04257 AST_LIST_REMOVE_CURRENT(&queues, list); 04258 if (!q->count) 04259 destroy_queue(q); 04260 else 04261 ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n"); 04262 } else { 04263 ast_mutex_lock(&q->lock); 04264 mem_iter = ao2_iterator_init(q->members, 0); 04265 while ((cur = ao2_iterator_next(&mem_iter))) { 04266 if (cur->dynamic) 04267 q->membercount++; 04268 cur->status = ast_device_state(cur->interface); 04269 ao2_ref(cur, -1); 04270 } 04271 ast_mutex_unlock(&q->lock); 04272 } 04273 } 04274 AST_LIST_TRAVERSE_SAFE_END; 04275 AST_LIST_UNLOCK(&queues); 04276 return 1; 04277 }
static int remove_from_interfaces | ( | const char * | interface | ) | [static] |
Definition at line 912 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().
00913 { 00914 struct member_interface *curint; 00915 00916 if (interface_exists_global(interface)) 00917 return 0; 00918 00919 AST_LIST_LOCK(&interfaces); 00920 AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) { 00921 if (!strcasecmp(curint->interface, interface)) { 00922 if (option_debug) 00923 ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface); 00924 AST_LIST_REMOVE_CURRENT(&interfaces, list); 00925 free(curint); 00926 break; 00927 } 00928 } 00929 AST_LIST_TRAVERSE_SAFE_END; 00930 AST_LIST_UNLOCK(&interfaces); 00931 00932 return 0; 00933 }
static int remove_from_queue | ( | const char * | queuename, | |
const char * | interface | |||
) | [static] |
Definition at line 3134 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().
03135 { 03136 struct call_queue *q; 03137 struct member *mem, tmpmem; 03138 int res = RES_NOSUCHQUEUE; 03139 03140 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 03141 03142 AST_LIST_LOCK(&queues); 03143 AST_LIST_TRAVERSE(&queues, q, list) { 03144 ast_mutex_lock(&q->lock); 03145 if (strcmp(q->name, queuename)) { 03146 ast_mutex_unlock(&q->lock); 03147 continue; 03148 } 03149 03150 if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) { 03151 /* XXX future changes should beware of this assumption!! */ 03152 if (!mem->dynamic) { 03153 res = RES_NOT_DYNAMIC; 03154 ao2_ref(mem, -1); 03155 ast_mutex_unlock(&q->lock); 03156 break; 03157 } 03158 q->membercount--; 03159 manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved", 03160 "Queue: %s\r\n" 03161 "Location: %s\r\n" 03162 "MemberName: %s\r\n", 03163 q->name, mem->interface, mem->membername); 03164 ao2_unlink(q->members, mem); 03165 ao2_ref(mem, -1); 03166 03167 if (queue_persistent_members) 03168 dump_queue_members(q); 03169 03170 res = RES_OKAY; 03171 } else { 03172 res = RES_EXISTS; 03173 } 03174 ast_mutex_unlock(&q->lock); 03175 break; 03176 } 03177 03178 if (res == RES_OKAY) 03179 remove_from_interfaces(interface); 03180 03181 AST_LIST_UNLOCK(&queues); 03182 03183 return res; 03184 }
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 1778 of file app_queue.c.
References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), 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, ast_channel::macrocontext, ast_channel::macroexten, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, ast_channel::priority, QUEUE_EVENT_VARIABLES, 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().
01779 { 01780 int res; 01781 int status; 01782 char tech[256]; 01783 char *location; 01784 01785 /* on entry here, we know that tmp->chan == NULL */ 01786 if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) { 01787 if (option_debug) 01788 ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s\n", tmp->interface); 01789 if (qe->chan->cdr) 01790 ast_cdr_busy(qe->chan->cdr); 01791 tmp->stillgoing = 0; 01792 (*busies)++; 01793 return 0; 01794 } 01795 01796 if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) { 01797 if (option_debug) 01798 ast_log(LOG_DEBUG, "%s in use, can't receive call\n", tmp->interface); 01799 if (qe->chan->cdr) 01800 ast_cdr_busy(qe->chan->cdr); 01801 tmp->stillgoing = 0; 01802 return 0; 01803 } 01804 01805 if (tmp->member->paused) { 01806 if (option_debug) 01807 ast_log(LOG_DEBUG, "%s paused, can't receive call\n", tmp->interface); 01808 if (qe->chan->cdr) 01809 ast_cdr_busy(qe->chan->cdr); 01810 tmp->stillgoing = 0; 01811 return 0; 01812 } 01813 if (use_weight && compare_weight(qe->parent,tmp->member)) { 01814 ast_log(LOG_DEBUG, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface); 01815 if (qe->chan->cdr) 01816 ast_cdr_busy(qe->chan->cdr); 01817 tmp->stillgoing = 0; 01818 (*busies)++; 01819 return 0; 01820 } 01821 01822 ast_copy_string(tech, tmp->interface, sizeof(tech)); 01823 if ((location = strchr(tech, '/'))) 01824 *location++ = '\0'; 01825 else 01826 location = ""; 01827 01828 /* Request the peer */ 01829 tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status); 01830 if (!tmp->chan) { /* If we can't, just go on to the next call */ 01831 if (qe->chan->cdr) 01832 ast_cdr_busy(qe->chan->cdr); 01833 tmp->stillgoing = 0; 01834 01835 update_status(tmp->member->interface, ast_device_state(tmp->member->interface)); 01836 01837 ast_mutex_lock(&qe->parent->lock); 01838 qe->parent->rrpos++; 01839 ast_mutex_unlock(&qe->parent->lock); 01840 01841 (*busies)++; 01842 return 0; 01843 } 01844 01845 tmp->chan->appl = "AppQueue"; 01846 tmp->chan->data = "(Outgoing Line)"; 01847 tmp->chan->whentohangup = 0; 01848 if (tmp->chan->cid.cid_num) 01849 free(tmp->chan->cid.cid_num); 01850 tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num); 01851 if (tmp->chan->cid.cid_name) 01852 free(tmp->chan->cid.cid_name); 01853 tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name); 01854 if (tmp->chan->cid.cid_ani) 01855 free(tmp->chan->cid.cid_ani); 01856 tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani); 01857 01858 /* Inherit specially named variables from parent channel */ 01859 ast_channel_inherit_variables(qe->chan, tmp->chan); 01860 01861 /* Presense of ADSI CPE on outgoing channel follows ours */ 01862 tmp->chan->adsicpe = qe->chan->adsicpe; 01863 01864 /* Inherit context and extension */ 01865 if (!ast_strlen_zero(qe->chan->macrocontext)) 01866 ast_copy_string(tmp->chan->dialcontext, qe->chan->macrocontext, sizeof(tmp->chan->dialcontext)); 01867 else 01868 ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext)); 01869 if (!ast_strlen_zero(qe->chan->macroexten)) 01870 ast_copy_string(tmp->chan->exten, qe->chan->macroexten, sizeof(tmp->chan->exten)); 01871 else 01872 ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten)); 01873 01874 /* Place the call, but don't wait on the answer */ 01875 if ((res = ast_call(tmp->chan, location, 0))) { 01876 /* Again, keep going even if there's an error */ 01877 if (option_debug) 01878 ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res); 01879 if (option_verbose > 2) 01880 ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface); 01881 do_hang(tmp); 01882 (*busies)++; 01883 return 0; 01884 } else if (qe->parent->eventwhencalled) { 01885 char vars[2048]; 01886 01887 manager_event(EVENT_FLAG_AGENT, "AgentCalled", 01888 "AgentCalled: %s\r\n" 01889 "AgentName: %s\r\n" 01890 "ChannelCalling: %s\r\n" 01891 "CallerID: %s\r\n" 01892 "CallerIDName: %s\r\n" 01893 "Context: %s\r\n" 01894 "Extension: %s\r\n" 01895 "Priority: %d\r\n" 01896 "%s", 01897 tmp->interface, tmp->member->membername, qe->chan->name, 01898 tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown", 01899 tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown", 01900 qe->chan->context, qe->chan->exten, qe->chan->priority, 01901 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 01902 if (option_verbose > 2) 01903 ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface); 01904 } 01905 01906 return 1; 01907 }
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 1933 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().
01934 { 01935 int ret = 0; 01936 01937 while (ret == 0) { 01938 struct callattempt *best = find_best(outgoing); 01939 if (!best) { 01940 if (option_debug) 01941 ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n"); 01942 break; 01943 } 01944 if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) { 01945 struct callattempt *cur; 01946 /* Ring everyone who shares this best metric (for ringall) */ 01947 for (cur = outgoing; cur; cur = cur->q_next) { 01948 if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) { 01949 if (option_debug) 01950 ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric); 01951 ret |= ring_entry(qe, cur, busies); 01952 } 01953 } 01954 } else { 01955 /* Ring just the best channel */ 01956 if (option_debug) 01957 ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric); 01958 ret = ring_entry(qe, best, busies); 01959 } 01960 } 01961 01962 return ret; 01963 }
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 2047 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.
02048 { 02049 if (option_verbose > 2) 02050 ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime); 02051 ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime); 02052 if (qe->parent->autopause) { 02053 if (!set_member_paused(qe->parent->name, interface, 1)) { 02054 if (option_verbose > 2) 02055 ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name); 02056 } else { 02057 if (option_verbose > 2) 02058 ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name); 02059 } 02060 } 02061 return; 02062 }
static int rqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3489 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().
03490 { 03491 int res=-1; 03492 struct ast_module_user *lu; 03493 char *parse, *temppos = NULL; 03494 int priority_jump = 0; 03495 AST_DECLARE_APP_ARGS(args, 03496 AST_APP_ARG(queuename); 03497 AST_APP_ARG(interface); 03498 AST_APP_ARG(options); 03499 ); 03500 03501 03502 if (ast_strlen_zero(data)) { 03503 ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n"); 03504 return -1; 03505 } 03506 03507 parse = ast_strdupa(data); 03508 03509 AST_STANDARD_APP_ARGS(args, parse); 03510 03511 lu = ast_module_user_add(chan); 03512 03513 if (ast_strlen_zero(args.interface)) { 03514 args.interface = ast_strdupa(chan->name); 03515 temppos = strrchr(args.interface, '-'); 03516 if (temppos) 03517 *temppos = '\0'; 03518 } 03519 03520 if (args.options) { 03521 if (strchr(args.options, 'j')) 03522 priority_jump = 1; 03523 } 03524 03525 switch (remove_from_queue(args.queuename, args.interface)) { 03526 case RES_OKAY: 03527 ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", ""); 03528 ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename); 03529 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED"); 03530 res = 0; 03531 break; 03532 case RES_EXISTS: 03533 ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename); 03534 if (priority_jump || ast_opt_priority_jumping) 03535 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101); 03536 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE"); 03537 res = 0; 03538 break; 03539 case RES_NOSUCHQUEUE: 03540 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename); 03541 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE"); 03542 res = 0; 03543 break; 03544 case RES_NOT_DYNAMIC: 03545 ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface); 03546 pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC"); 03547 res = 0; 03548 break; 03549 } 03550 03551 ast_module_user_remove(lu); 03552 03553 return res; 03554 }
static void rr_dep_warning | ( | void | ) | [static] |
Definition at line 444 of file app_queue.c.
References ast_log(), and LOG_NOTICE.
Referenced by reload_queues().
00445 { 00446 static unsigned int warned = 0; 00447 00448 if (!warned) { 00449 ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n"); 00450 warned = 1; 00451 } 00452 }
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 1100 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().
01101 { 01102 struct member *m, tmpmem; 01103 int penalty = 0; 01104 int paused = 0; 01105 01106 if (penalty_str) { 01107 penalty = atoi(penalty_str); 01108 if (penalty < 0) 01109 penalty = 0; 01110 } 01111 01112 if (paused_str) { 01113 paused = atoi(paused_str); 01114 if (paused < 0) 01115 paused = 0; 01116 } 01117 01118 /* Find the member, or the place to put a new one. */ 01119 ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface)); 01120 m = ao2_find(q->members, &tmpmem, OBJ_POINTER); 01121 01122 /* Create a new one if not found, else update penalty */ 01123 if (!m) { 01124 if ((m = create_queue_member(interface, membername, penalty, paused))) { 01125 m->dead = 0; 01126 m->realtime = 1; 01127 add_to_interfaces(interface); 01128 ao2_link(q->members, m); 01129 ao2_ref(m, -1); 01130 m = NULL; 01131 q->membercount++; 01132 } 01133 } else { 01134 m->dead = 0; /* Do not delete this one. */ 01135 if (paused_str) 01136 m->paused = paused; 01137 m->penalty = penalty; 01138 ao2_ref(m, -1); 01139 } 01140 }
static int say_periodic_announcement | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1989 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().
01990 { 01991 int res = 0; 01992 time_t now; 01993 01994 /* Get the current time */ 01995 time(&now); 01996 01997 /* Check to see if it is time to announce */ 01998 if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency) 01999 return 0; 02000 02001 /* Stop the music on hold so we can play our own file */ 02002 ast_moh_stop(qe->chan); 02003 02004 if (option_verbose > 2) 02005 ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n"); 02006 02007 /* Check to make sure we have a sound file. If not, reset to the first sound file */ 02008 if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) { 02009 qe->last_periodic_announce_sound = 0; 02010 } 02011 02012 /* play the announcement */ 02013 res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]); 02014 02015 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 02016 res = 0; 02017 02018 /* Resume Music on Hold if the caller is going to stay in the queue */ 02019 if (!res) 02020 ast_moh_start(qe->chan, qe->moh, NULL); 02021 02022 /* update last_periodic_announce_time */ 02023 qe->last_periodic_announce_time = now; 02024 02025 /* Update the current periodic announcement to the next announcement */ 02026 qe->last_periodic_announce_sound++; 02027 02028 return res; 02029 }
static int say_position | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 1516 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().
01517 { 01518 int res = 0, avgholdmins, avgholdsecs; 01519 time_t now; 01520 01521 /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/ 01522 time(&now); 01523 if ((now - qe->last_pos) < 15) 01524 return 0; 01525 01526 /* If either our position has changed, or we are over the freq timer, say position */ 01527 if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency)) 01528 return 0; 01529 01530 ast_moh_stop(qe->chan); 01531 /* Say we're next, if we are */ 01532 if (qe->pos == 1) { 01533 res = play_file(qe->chan, qe->parent->sound_next); 01534 if (res) 01535 goto playout; 01536 else 01537 goto posout; 01538 } else { 01539 res = play_file(qe->chan, qe->parent->sound_thereare); 01540 if (res) 01541 goto playout; 01542 res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */ 01543 if (res) 01544 goto playout; 01545 res = play_file(qe->chan, qe->parent->sound_calls); 01546 if (res) 01547 goto playout; 01548 } 01549 /* Round hold time to nearest minute */ 01550 avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60); 01551 01552 /* If they have specified a rounding then round the seconds as well */ 01553 if (qe->parent->roundingseconds) { 01554 avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds; 01555 avgholdsecs *= qe->parent->roundingseconds; 01556 } else { 01557 avgholdsecs = 0; 01558 } 01559 01560 if (option_verbose > 2) 01561 ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs); 01562 01563 /* If the hold time is >1 min, if it's enabled, and if it's not 01564 supposed to be only once and we have already said it, say it */ 01565 if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) && 01566 (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) { 01567 res = play_file(qe->chan, qe->parent->sound_holdtime); 01568 if (res) 01569 goto playout; 01570 01571 if (avgholdmins > 0) { 01572 if (avgholdmins < 2) { 01573 res = play_file(qe->chan, qe->parent->sound_lessthan); 01574 if (res) 01575 goto playout; 01576 01577 res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL); 01578 if (res) 01579 goto playout; 01580 } else { 01581 res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL); 01582 if (res) 01583 goto playout; 01584 } 01585 01586 res = play_file(qe->chan, qe->parent->sound_minutes); 01587 if (res) 01588 goto playout; 01589 } 01590 if (avgholdsecs>0) { 01591 res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL); 01592 if (res) 01593 goto playout; 01594 01595 res = play_file(qe->chan, qe->parent->sound_seconds); 01596 if (res) 01597 goto playout; 01598 } 01599 01600 } 01601 01602 posout: 01603 if (option_verbose > 2) 01604 ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n", 01605 qe->chan->name, qe->parent->name, qe->pos); 01606 res = play_file(qe->chan, qe->parent->sound_thanks); 01607 01608 playout: 01609 if ((res > 0 && !valid_exit(qe, res)) || res < 0) 01610 res = 0; 01611 01612 /* Set our last_pos indicators */ 01613 qe->last_pos = now; 01614 qe->last_pos_said = qe->pos; 01615 01616 /* Don't restart music on hold if we're about to exit the caller from the queue */ 01617 if (!res) 01618 ast_moh_start(qe->chan, qe->moh, NULL); 01619 01620 return res; 01621 }
static int set_member_paused | ( | const char * | queuename, | |
const char * | interface, | |||
int | paused | |||
) | [static] |
Definition at line 3242 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().
03243 { 03244 int found = 0; 03245 struct call_queue *q; 03246 struct member *mem; 03247 03248 /* Special event for when all queues are paused - individual events still generated */ 03249 /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */ 03250 if (ast_strlen_zero(queuename)) 03251 ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", ""); 03252 03253 AST_LIST_LOCK(&queues); 03254 AST_LIST_TRAVERSE(&queues, q, list) { 03255 ast_mutex_lock(&q->lock); 03256 if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) { 03257 if ((mem = interface_exists(q, interface))) { 03258 found++; 03259 if (mem->paused == paused) 03260 ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface); 03261 mem->paused = paused; 03262 03263 if (queue_persistent_members) 03264 dump_queue_members(q); 03265 03266 if (mem->realtime) 03267 update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0"); 03268 03269 ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", ""); 03270 03271 manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused", 03272 "Queue: %s\r\n" 03273 "Location: %s\r\n" 03274 "MemberName: %s\r\n" 03275 "Paused: %d\r\n", 03276 q->name, mem->interface, mem->membername, paused); 03277 ao2_ref(mem, -1); 03278 } 03279 } 03280 ast_mutex_unlock(&q->lock); 03281 } 03282 AST_LIST_UNLOCK(&queues); 03283 03284 return found ? RESULT_SUCCESS : RESULT_FAILURE; 03285 }
static void set_queue_result | ( | struct ast_channel * | chan, | |
enum queue_result | res | |||
) | [static] |
sets the QUEUESTATUS channel variable
Definition at line 463 of file app_queue.c.
References queue_ent::chan, pbx_builtin_setvar_helper(), queue_results, and text.
Referenced by queue_exec().
00464 { 00465 int i; 00466 00467 for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) { 00468 if (queue_results[i].id == res) { 00469 pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text); 00470 return; 00471 } 00472 } 00473 }
static int statechange_queue | ( | const char * | dev, | |
int | state, | |||
void * | ign | |||
) | [static] |
Producer of the statechange queue.
Definition at line 724 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().
00725 { 00726 struct statechange *sc; 00727 00728 if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1))) 00729 return 0; 00730 00731 sc->state = state; 00732 strcpy(sc->dev, dev); 00733 00734 ast_mutex_lock(&device_state.lock); 00735 AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry); 00736 ast_cond_signal(&device_state.cond); 00737 ast_mutex_unlock(&device_state.lock); 00738 00739 return 0; 00740 }
static int store_next | ( | struct queue_ent * | qe, | |
struct callattempt * | outgoing | |||
) | [static] |
Definition at line 1965 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().
01966 { 01967 struct callattempt *best = find_best(outgoing); 01968 01969 if (best) { 01970 /* Ring just the best channel */ 01971 if (option_debug) 01972 ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric); 01973 qe->parent->rrpos = best->metric % 1000; 01974 } else { 01975 /* Just increment rrpos */ 01976 if (qe->parent->wrapped) { 01977 /* No more channels, start over */ 01978 qe->parent->rrpos = 0; 01979 } else { 01980 /* Prioritize next entry */ 01981 qe->parent->rrpos++; 01982 } 01983 } 01984 qe->parent->wrapped = 0; 01985 01986 return 0; 01987 }
static int strat2int | ( | const char * | strategy | ) | [static] |
Definition at line 487 of file app_queue.c.
References name, and strategies.
Referenced by queue_set_param().
00488 { 00489 int x; 00490 00491 for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) { 00492 if (!strcasecmp(strategy, strategies[x].name)) 00493 return strategies[x].strategy; 00494 } 00495 00496 return -1; 00497 }
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 2558 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_LOCKED, 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, 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, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), call_queue::servicelevel, call_queue::setinterfacevar, 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, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), and wait_for_answer().
Referenced by queue_exec().
02559 { 02560 struct member *cur; 02561 struct callattempt *outgoing = NULL; /* the list of calls we are building */ 02562 int to; 02563 char oldexten[AST_MAX_EXTENSION]=""; 02564 char oldcontext[AST_MAX_CONTEXT]=""; 02565 char queuename[256]=""; 02566 struct ast_channel *peer; 02567 struct ast_channel *which; 02568 struct callattempt *lpeer; 02569 struct member *member; 02570 struct ast_app *app; 02571 int res = 0, bridge = 0; 02572 int numbusies = 0; 02573 int x=0; 02574 char *announce = NULL; 02575 char digit = 0; 02576 time_t callstart; 02577 time_t now = time(NULL); 02578 struct ast_bridge_config bridge_config; 02579 char nondataquality = 1; 02580 char *agiexec = NULL; 02581 int ret = 0; 02582 const char *monitorfilename; 02583 const char *monitor_exec; 02584 const char *monitor_options; 02585 char tmpid[256], tmpid2[256]; 02586 char meid[1024], meid2[1024]; 02587 char mixmonargs[1512]; 02588 struct ast_app *mixmonapp = NULL; 02589 char *p; 02590 char vars[2048]; 02591 int forwardsallowed = 1; 02592 int callcompletedinsl; 02593 struct ao2_iterator memi; 02594 struct ast_datastore *datastore; 02595 02596 ast_channel_lock(qe->chan); 02597 datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL); 02598 ast_channel_unlock(qe->chan); 02599 02600 memset(&bridge_config, 0, sizeof(bridge_config)); 02601 time(&now); 02602 02603 for (; options && *options; options++) 02604 switch (*options) { 02605 case 't': 02606 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT); 02607 break; 02608 case 'T': 02609 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT); 02610 break; 02611 case 'w': 02612 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON); 02613 break; 02614 case 'W': 02615 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON); 02616 break; 02617 case 'd': 02618 nondataquality = 0; 02619 break; 02620 case 'h': 02621 ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT); 02622 break; 02623 case 'H': 02624 ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT); 02625 break; 02626 case 'n': 02627 if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) 02628 (*tries)++; 02629 else 02630 *tries = qe->parent->membercount; 02631 *noption = 1; 02632 break; 02633 case 'i': 02634 forwardsallowed = 0; 02635 break; 02636 } 02637 02638 /* Hold the lock while we setup the outgoing calls */ 02639 if (use_weight) 02640 AST_LIST_LOCK(&queues); 02641 ast_mutex_lock(&qe->parent->lock); 02642 if (option_debug) 02643 ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n", 02644 qe->chan->name); 02645 ast_copy_string(queuename, qe->parent->name, sizeof(queuename)); 02646 if (!ast_strlen_zero(qe->announce)) 02647 announce = qe->announce; 02648 if (!ast_strlen_zero(announceoverride)) 02649 announce = announceoverride; 02650 02651 memi = ao2_iterator_init(qe->parent->members, 0); 02652 while ((cur = ao2_iterator_next(&memi))) { 02653 struct callattempt *tmp = ast_calloc(1, sizeof(*tmp)); 02654 struct ast_dialed_interface *di; 02655 AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces; 02656 if (!tmp) { 02657 ao2_ref(cur, -1); 02658 ast_mutex_unlock(&qe->parent->lock); 02659 if (use_weight) 02660 AST_LIST_UNLOCK(&queues); 02661 goto out; 02662 } 02663 if (!datastore) { 02664 if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) { 02665 ao2_ref(cur, -1); 02666 ast_mutex_unlock(&qe->parent->lock); 02667 if (use_weight) 02668 AST_LIST_UNLOCK(&queues); 02669 free(tmp); 02670 goto out; 02671 } 02672 datastore->inheritance = DATASTORE_INHERIT_FOREVER; 02673 if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) { 02674 ao2_ref(cur, -1); 02675 ast_mutex_unlock(&qe->parent->lock); 02676 if (use_weight) 02677 AST_LIST_UNLOCK(&queues); 02678 free(tmp); 02679 goto out; 02680 } 02681 datastore->data = dialed_interfaces; 02682 AST_LIST_HEAD_INIT(dialed_interfaces); 02683 02684 ast_channel_lock(qe->chan); 02685 ast_channel_datastore_add(qe->chan, datastore); 02686 ast_channel_unlock(qe->chan); 02687 } else 02688 dialed_interfaces = datastore->data; 02689 02690 AST_LIST_LOCK(dialed_interfaces); 02691 AST_LIST_TRAVERSE(dialed_interfaces, di, list) { 02692 if (!strcasecmp(cur->interface, di->interface)) { 02693 ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 02694 di->interface); 02695 break; 02696 } 02697 } 02698 AST_LIST_UNLOCK(dialed_interfaces); 02699 02700 if (di) { 02701 free(tmp); 02702 continue; 02703 } 02704 02705 /* It is always ok to dial a Local interface. We only keep track of 02706 * which "real" interfaces have been dialed. The Local channel will 02707 * inherit this list so that if it ends up dialing a real interface, 02708 * it won't call one that has already been called. */ 02709 if (strncasecmp(cur->interface, "Local/", 6)) { 02710 if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) { 02711 ao2_ref(cur, -1); 02712 ast_mutex_unlock(&qe->parent->lock); 02713 if (use_weight) 02714 AST_LIST_UNLOCK(&queues); 02715 free(tmp); 02716 goto out; 02717 } 02718 strcpy(di->interface, cur->interface); 02719 02720 AST_LIST_LOCK(dialed_interfaces); 02721 AST_LIST_INSERT_TAIL(dialed_interfaces, di, list); 02722 AST_LIST_UNLOCK(dialed_interfaces); 02723 } 02724 02725 tmp->stillgoing = -1; 02726 tmp->member = cur; 02727 tmp->oldstatus = cur->status; 02728 tmp->lastcall = cur->lastcall; 02729 ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface)); 02730 /* Special case: If we ring everyone, go ahead and ring them, otherwise 02731 just calculate their metric for the appropriate strategy */ 02732 if (!calc_metric(qe->parent, cur, x++, qe, tmp)) { 02733 /* Put them in the list of outgoing thingies... We're ready now. 02734 XXX If we're forcibly removed, these outgoing calls won't get 02735 hung up XXX */ 02736 tmp->q_next = outgoing; 02737 outgoing = tmp; 02738 /* If this line is up, don't try anybody else */ 02739 if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP)) 02740 break; 02741 } else { 02742 ao2_ref(cur, -1); 02743 free(tmp); 02744 } 02745 } 02746 if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout)) 02747 to = (qe->expire - now) * 1000; 02748 else 02749 to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1; 02750 ++qe->pending; 02751 ast_mutex_unlock(&qe->parent->lock); 02752 ring_one(qe, outgoing, &numbusies); 02753 if (use_weight) 02754 AST_LIST_UNLOCK(&queues); 02755 lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed); 02756 if (datastore) { 02757 ast_channel_datastore_remove(qe->chan, datastore); 02758 ast_channel_datastore_free(datastore); 02759 } 02760 ast_mutex_lock(&qe->parent->lock); 02761 if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) { 02762 store_next(qe, outgoing); 02763 } 02764 ast_mutex_unlock(&qe->parent->lock); 02765 peer = lpeer ? lpeer->chan : NULL; 02766 if (!peer) { 02767 qe->pending = 0; 02768 if (to) { 02769 /* Must gotten hung up */ 02770 res = -1; 02771 } else { 02772 /* User exited by pressing a digit */ 02773 res = digit; 02774 } 02775 if (option_debug && res == -1) 02776 ast_log(LOG_DEBUG, "%s: Nobody answered.\n", qe->chan->name); 02777 } else { /* peer is valid */ 02778 /* Ah ha! Someone answered within the desired timeframe. Of course after this 02779 we will always return with -1 so that it is hung up properly after the 02780 conversation. */ 02781 qe->handled++; 02782 if (!strcmp(qe->chan->tech->type, "Zap")) 02783 ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 02784 if (!strcmp(peer->tech->type, "Zap")) 02785 ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0); 02786 /* Update parameters for the queue */ 02787 time(&now); 02788 recalc_holdtime(qe, (now - qe->start)); 02789 ast_mutex_lock(&qe->parent->lock); 02790 callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel); 02791 ast_mutex_unlock(&qe->parent->lock); 02792 member = lpeer->member; 02793 /* Increment the refcount for this member, since we're going to be using it for awhile in here. */ 02794 ao2_ref(member, 1); 02795 hangupcalls(outgoing, peer); 02796 outgoing = NULL; 02797 if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) { 02798 int res2; 02799 02800 res2 = ast_autoservice_start(qe->chan); 02801 if (!res2) { 02802 if (qe->parent->memberdelay) { 02803 ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay); 02804 res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000); 02805 } 02806 if (!res2 && announce) { 02807 play_file(peer, announce); 02808 } 02809 if (!res2 && qe->parent->reportholdtime) { 02810 if (!play_file(peer, qe->parent->sound_reporthold)) { 02811 int holdtime; 02812 02813 time(&now); 02814 holdtime = abs((now - qe->start) / 60); 02815 if (holdtime < 2) { 02816 play_file(peer, qe->parent->sound_lessthan); 02817 ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL); 02818 } else 02819 ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL); 02820 play_file(peer, qe->parent->sound_minutes); 02821 } 02822 } 02823 } 02824 res2 |= ast_autoservice_stop(qe->chan); 02825 if (peer->_softhangup) { 02826 /* Agent must have hung up */ 02827 ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name); 02828 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", ""); 02829 record_abandoned(qe); 02830 if (qe->parent->eventwhencalled) 02831 manager_event(EVENT_FLAG_AGENT, "AgentDump", 02832 "Queue: %s\r\n" 02833 "Uniqueid: %s\r\n" 02834 "Channel: %s\r\n" 02835 "Member: %s\r\n" 02836 "MemberName: %s\r\n" 02837 "%s", 02838 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 02839 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 02840 ast_hangup(peer); 02841 ao2_ref(member, -1); 02842 goto out; 02843 } else if (res2) { 02844 /* Caller must have hung up just before being connected*/ 02845 ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name); 02846 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02847 record_abandoned(qe); 02848 ast_hangup(peer); 02849 ao2_ref(member, -1); 02850 return -1; 02851 } 02852 } 02853 /* Stop music on hold */ 02854 ast_moh_stop(qe->chan); 02855 /* If appropriate, log that we have a destination channel */ 02856 if (qe->chan->cdr) 02857 ast_cdr_setdestchan(qe->chan->cdr, peer->name); 02858 /* Make sure channels are compatible */ 02859 res = ast_channel_make_compatible(qe->chan, peer); 02860 if (res < 0) { 02861 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", ""); 02862 ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name); 02863 record_abandoned(qe); 02864 ast_hangup(peer); 02865 ao2_ref(member, -1); 02866 return -1; 02867 } 02868 02869 if (qe->parent->setinterfacevar) 02870 pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface); 02871 02872 /* Begin Monitoring */ 02873 if (qe->parent->monfmt && *qe->parent->monfmt) { 02874 if (!qe->parent->montype) { 02875 if (option_debug) 02876 ast_log(LOG_DEBUG, "Starting Monitor as requested.\n"); 02877 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 02878 if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS")) 02879 which = qe->chan; 02880 else 02881 which = peer; 02882 if (monitorfilename) 02883 ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 ); 02884 else if (qe->chan->cdr) 02885 ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 ); 02886 else { 02887 /* Last ditch effort -- no CDR, make up something */ 02888 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 02889 ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 ); 02890 } 02891 if (qe->parent->monjoin) 02892 ast_monitor_setjoinfiles(which, 1); 02893 } else { 02894 if (option_debug) 02895 ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n"); 02896 monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"); 02897 if (!monitorfilename) { 02898 if (qe->chan->cdr) 02899 ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1); 02900 else 02901 snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); 02902 } else { 02903 ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1); 02904 for (p = tmpid2; *p ; p++) { 02905 if (*p == '^' && *(p+1) == '{') { 02906 *p = '$'; 02907 } 02908 } 02909 02910 memset(tmpid, 0, sizeof(tmpid)); 02911 pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1); 02912 } 02913 02914 monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC"); 02915 monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS"); 02916 02917 if (monitor_exec) { 02918 ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1); 02919 for (p = meid2; *p ; p++) { 02920 if (*p == '^' && *(p+1) == '{') { 02921 *p = '$'; 02922 } 02923 } 02924 02925 memset(meid, 0, sizeof(meid)); 02926 pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1); 02927 } 02928 02929 snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt); 02930 02931 mixmonapp = pbx_findapp("MixMonitor"); 02932 02933 if (strchr(tmpid2, '|')) { 02934 ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n"); 02935 mixmonapp = NULL; 02936 } 02937 02938 if (!monitor_options) 02939 monitor_options = ""; 02940 02941 if (strchr(monitor_options, '|')) { 02942 ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n"); 02943 mixmonapp = NULL; 02944 } 02945 02946 if (mixmonapp) { 02947 if (!ast_strlen_zero(monitor_exec)) 02948 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec); 02949 else 02950 snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options); 02951 02952 if (option_debug) 02953 ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs); 02954 /* We purposely lock the CDR so that pbx_exec does not update the application data */ 02955 if (qe->chan->cdr) 02956 ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 02957 ret = pbx_exec(qe->chan, mixmonapp, mixmonargs); 02958 if (qe->chan->cdr) 02959 ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED); 02960 02961 } else 02962 ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n"); 02963 02964 } 02965 } 02966 /* Drop out of the queue at this point, to prepare for next caller */ 02967 leave_queue(qe); 02968 if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) { 02969 if (option_debug) 02970 ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url); 02971 ast_channel_sendurl(peer, url); 02972 } 02973 if (!ast_strlen_zero(agi)) { 02974 if (option_debug) 02975 ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi); 02976 app = pbx_findapp("agi"); 02977 if (app) { 02978 agiexec = ast_strdupa(agi); 02979 ret = pbx_exec(qe->chan, app, agiexec); 02980 } else 02981 ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n"); 02982 } 02983 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid); 02984 if (qe->parent->eventwhencalled) 02985 manager_event(EVENT_FLAG_AGENT, "AgentConnect", 02986 "Queue: %s\r\n" 02987 "Uniqueid: %s\r\n" 02988 "Channel: %s\r\n" 02989 "Member: %s\r\n" 02990 "MemberName: %s\r\n" 02991 "Holdtime: %ld\r\n" 02992 "BridgedChannel: %s\r\n" 02993 "%s", 02994 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 02995 (long)time(NULL) - qe->start, peer->uniqueid, 02996 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 02997 ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext)); 02998 ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten)); 02999 time(&callstart); 03000 03001 if (member->status == AST_DEVICE_NOT_INUSE) 03002 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); 03003 03004 03005 bridge = ast_bridge_call(qe->chan,peer, &bridge_config); 03006 03007 if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) { 03008 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld", 03009 qe->chan->exten, qe->chan->context, (long) (callstart - qe->start), 03010 (long) (time(NULL) - callstart)); 03011 } else if (qe->chan->_softhangup) { 03012 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d", 03013 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03014 if (qe->parent->eventwhencalled) 03015 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03016 "Queue: %s\r\n" 03017 "Uniqueid: %s\r\n" 03018 "Channel: %s\r\n" 03019 "Member: %s\r\n" 03020 "MemberName: %s\r\n" 03021 "HoldTime: %ld\r\n" 03022 "TalkTime: %ld\r\n" 03023 "Reason: caller\r\n" 03024 "%s", 03025 queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, 03026 (long)(callstart - qe->start), (long)(time(NULL) - callstart), 03027 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03028 } else { 03029 ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d", 03030 (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos); 03031 if (qe->parent->eventwhencalled) 03032 manager_event(EVENT_FLAG_AGENT, "AgentComplete", 03033 "Queue: %s\r\n" 03034 "Uniqueid: %s\r\n" 03035 "Channel: %s\r\n" 03036 "MemberName: %s\r\n" 03037 "HoldTime: %ld\r\n" 03038 "TalkTime: %ld\r\n" 03039 "Reason: agent\r\n" 03040 "%s", 03041 queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start), 03042 (long)(time(NULL) - callstart), 03043 qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : ""); 03044 } 03045 03046 if (bridge != AST_PBX_NO_HANGUP_PEER) 03047 ast_hangup(peer); 03048 update_queue(qe->parent, member, callcompletedinsl); 03049 res = bridge ? bridge : 1; 03050 ao2_ref(member, -1); 03051 } 03052 out: 03053 hangupcalls(outgoing, NULL); 03054 03055 return res; 03056 }
static int unload_module | ( | void | ) | [static] |
Definition at line 4863 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().
04864 { 04865 int res; 04866 04867 if (device_state.thread != AST_PTHREADT_NULL) { 04868 device_state.stop = 1; 04869 ast_mutex_lock(&device_state.lock); 04870 ast_cond_signal(&device_state.cond); 04871 ast_mutex_unlock(&device_state.lock); 04872 pthread_join(device_state.thread, NULL); 04873 } 04874 04875 ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry)); 04876 res = ast_manager_unregister("QueueStatus"); 04877 res |= ast_manager_unregister("Queues"); 04878 res |= ast_manager_unregister("QueueStatus"); 04879 res |= ast_manager_unregister("QueueAdd"); 04880 res |= ast_manager_unregister("QueueRemove"); 04881 res |= ast_manager_unregister("QueuePause"); 04882 res |= ast_unregister_application(app_aqm); 04883 res |= ast_unregister_application(app_rqm); 04884 res |= ast_unregister_application(app_pqm); 04885 res |= ast_unregister_application(app_upqm); 04886 res |= ast_unregister_application(app_ql); 04887 res |= ast_unregister_application(app); 04888 res |= ast_custom_function_unregister(&queueagentcount_function); 04889 res |= ast_custom_function_unregister(&queuemembercount_function); 04890 res |= ast_custom_function_unregister(&queuememberlist_function); 04891 res |= ast_custom_function_unregister(&queuewaitingcount_function); 04892 ast_devstate_del(statechange_queue, NULL); 04893 04894 ast_module_user_hangup_all(); 04895 04896 clear_and_free_interfaces(); 04897 04898 return res; 04899 }
static int update_queue | ( | struct call_queue * | q, | |
struct member * | member, | |||
int | callcompletedinsl | |||
) | [static] |
Definition at line 2462 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().
02463 { 02464 ast_mutex_lock(&q->lock); 02465 time(&member->lastcall); 02466 member->calls++; 02467 q->callscompleted++; 02468 if (callcompletedinsl) 02469 q->callscompletedinsl++; 02470 ast_mutex_unlock(&q->lock); 02471 return 0; 02472 }
static int update_realtime_member_field | ( | struct member * | mem, | |
const char * | queue_name, | |||
const char * | field, | |||
const char * | value | |||
) | [static] |
Definition at line 1287 of file app_queue.c.
References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), member::interface, and var.
Referenced by set_member_paused().
01288 { 01289 struct ast_variable *var; 01290 int ret = -1; 01291 01292 if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 01293 return ret; 01294 while (var) { 01295 if (!strcmp(var->name, "uniqueid")) 01296 break; 01297 var = var->next; 01298 } 01299 if (var && !ast_strlen_zero(var->value)) { 01300 if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1) 01301 ret = 0; 01302 } 01303 return ret; 01304 }
static void update_realtime_members | ( | struct call_queue * | q | ) | [static] |
Definition at line 1306 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().
01307 { 01308 struct ast_config *member_config = NULL; 01309 struct member *m; 01310 char *interface = NULL; 01311 struct ao2_iterator mem_iter; 01312 01313 if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) { 01314 /*This queue doesn't have realtime members*/ 01315 if (option_debug > 2) 01316 ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name); 01317 return; 01318 } 01319 01320 ast_mutex_lock(&q->lock); 01321 01322 /* Temporarily set realtime members dead so we can detect deleted ones.*/ 01323 mem_iter = ao2_iterator_init(q->members, 0); 01324 while ((m = ao2_iterator_next(&mem_iter))) { 01325 if (m->realtime) 01326 m->dead = 1; 01327 ao2_ref(m, -1); 01328 } 01329 01330 while ((interface = ast_category_browse(member_config, interface))) { 01331 rt_handle_member_record(q, interface, 01332 S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface), 01333 ast_variable_retrieve(member_config, interface, "penalty"), 01334 ast_variable_retrieve(member_config, interface, "paused")); 01335 } 01336 01337 /* Delete all realtime members that have been deleted in DB. */ 01338 mem_iter = ao2_iterator_init(q->members, 0); 01339 while ((m = ao2_iterator_next(&mem_iter))) { 01340 if (m->dead) { 01341 ao2_unlink(q->members, m); 01342 ast_mutex_unlock(&q->lock); 01343 remove_from_interfaces(m->interface); 01344 ast_mutex_lock(&q->lock); 01345 q->membercount--; 01346 } 01347 ao2_ref(m, -1); 01348 } 01349 ast_mutex_unlock(&q->lock); 01350 ast_config_destroy(member_config); 01351 }
static int update_status | ( | const char * | interface, | |
const int | status | |||
) | [static] |
Definition at line 576 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().
00577 { 00578 struct member *cur; 00579 struct ao2_iterator mem_iter; 00580 struct call_queue *q; 00581 00582 AST_LIST_LOCK(&queues); 00583 AST_LIST_TRAVERSE(&queues, q, list) { 00584 ast_mutex_lock(&q->lock); 00585 mem_iter = ao2_iterator_init(q->members, 0); 00586 while ((cur = ao2_iterator_next(&mem_iter))) { 00587 char *tmp_interface; 00588 char *slash_pos; 00589 tmp_interface = ast_strdupa(cur->interface); 00590 if ((slash_pos = strchr(tmp_interface, '/'))) 00591 if ((slash_pos = strchr(slash_pos + 1, '/'))) 00592 *slash_pos = '\0'; 00593 00594 if (strcasecmp(interface, tmp_interface)) { 00595 ao2_ref(cur, -1); 00596 continue; 00597 } 00598 00599 if (cur->status != status) { 00600 cur->status = status; 00601 if (q->maskmemberstatus) { 00602 ao2_ref(cur, -1); 00603 continue; 00604 } 00605 00606 manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus", 00607 "Queue: %s\r\n" 00608 "Location: %s\r\n" 00609 "MemberName: %s\r\n" 00610 "Membership: %s\r\n" 00611 "Penalty: %d\r\n" 00612 "CallsTaken: %d\r\n" 00613 "LastCall: %d\r\n" 00614 "Status: %d\r\n" 00615 "Paused: %d\r\n", 00616 q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static", 00617 cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused); 00618 } 00619 ao2_ref(cur, -1); 00620 } 00621 ast_mutex_unlock(&q->lock); 00622 } 00623 AST_LIST_UNLOCK(&queues); 00624 00625 return 0; 00626 }
static int upqm_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 3436 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, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), and set_member_paused().
Referenced by load_module().
03437 { 03438 struct ast_module_user *lu; 03439 char *parse; 03440 int priority_jump = 0; 03441 AST_DECLARE_APP_ARGS(args, 03442 AST_APP_ARG(queuename); 03443 AST_APP_ARG(interface); 03444 AST_APP_ARG(options); 03445 ); 03446 03447 if (ast_strlen_zero(data)) { 03448 ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n"); 03449 return -1; 03450 } 03451 03452 parse = ast_strdupa(data); 03453 03454 AST_STANDARD_APP_ARGS(args, parse); 03455 03456 lu = ast_module_user_add(chan); 03457 03458 if (args.options) { 03459 if (strchr(args.options, 'j')) 03460 priority_jump = 1; 03461 } 03462 03463 if (ast_strlen_zero(args.interface)) { 03464 ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n"); 03465 ast_module_user_remove(lu); 03466 return -1; 03467 } 03468 03469 if (set_member_paused(args.queuename, args.interface, 0)) { 03470 ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface); 03471 if (priority_jump || ast_opt_priority_jumping) { 03472 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) { 03473 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 03474 ast_module_user_remove(lu); 03475 return 0; 03476 } 03477 } 03478 ast_module_user_remove(lu); 03479 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND"); 03480 return 0; 03481 } 03482 03483 ast_module_user_remove(lu); 03484 pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED"); 03485 03486 return 0; 03487 }
static int valid_exit | ( | struct queue_ent * | qe, | |
char | digit | |||
) | [static] |
Definition at line 1483 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().
01484 { 01485 int digitlen = strlen(qe->digits); 01486 01487 /* Prevent possible buffer overflow */ 01488 if (digitlen < sizeof(qe->digits) - 2) { 01489 qe->digits[digitlen] = digit; 01490 qe->digits[digitlen + 1] = '\0'; 01491 } else { 01492 qe->digits[0] = '\0'; 01493 return 0; 01494 } 01495 01496 /* If there's no context to goto, short-circuit */ 01497 if (ast_strlen_zero(qe->context)) 01498 return 0; 01499 01500 /* If the extension is bad, then reset the digits to blank */ 01501 if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) { 01502 qe->digits[0] = '\0'; 01503 return 0; 01504 } 01505 01506 /* We have an exact match */ 01507 if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) { 01508 qe->valid_digits = 1; 01509 /* Return 1 on a successful goto */ 01510 return 1; 01511 } 01512 01513 return 0; 01514 }
static char* vars2manager | ( | struct ast_channel * | chan, | |
char * | vars, | |||
size_t | len | |||
) | [static] |
Definition at line 1738 of file app_queue.c.
References pbx_builtin_serialize_variables().
Referenced by ring_entry(), and try_calling().
01739 { 01740 char *tmp = alloca(len); 01741 01742 if (pbx_builtin_serialize_variables(chan, tmp, len)) { 01743 int i, j; 01744 01745 /* convert "\n" to "\nVariable: " */ 01746 strcpy(vars, "Variable: "); 01747 01748 for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) { 01749 vars[j] = tmp[i]; 01750 01751 if (tmp[i + 1] == '\0') 01752 break; 01753 if (tmp[i] == '\n') { 01754 vars[j++] = '\r'; 01755 vars[j++] = '\n'; 01756 01757 ast_copy_string(&(vars[j]), "Variable: ", len - j); 01758 j += 9; 01759 } 01760 } 01761 if (j > len - 1) 01762 j = len - 1; 01763 vars[j - 2] = '\r'; 01764 vars[j - 1] = '\n'; 01765 vars[j] = '\0'; 01766 } else { 01767 /* there are no channel variables; leave it blank */ 01768 *vars = '\0'; 01769 } 01770 return vars; 01771 }
static int wait_a_bit | ( | struct queue_ent * | qe | ) | [static] |
Definition at line 3058 of file app_queue.c.
References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().
Referenced by queue_exec().
03059 { 03060 /* Don't need to hold the lock while we setup the outgoing calls */ 03061 int retrywait = qe->parent->retry * 1000; 03062 03063 int res = ast_waitfordigit(qe->chan, retrywait); 03064 if (res > 0 && !valid_exit(qe, res)) 03065 res = 0; 03066 03067 return res; 03068 }
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 2075 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.
02076 { 02077 char *queue = qe->parent->name; 02078 struct callattempt *o, *start = NULL, *prev = NULL; 02079 int status; 02080 int numbusies = prebusies; 02081 int numnochan = 0; 02082 int stillgoing = 0; 02083 int orig = *to; 02084 struct ast_frame *f; 02085 struct callattempt *peer = NULL; 02086 struct ast_channel *winner; 02087 struct ast_channel *in = qe->chan; 02088 char on[80] = ""; 02089 char membername[80] = ""; 02090 long starttime = 0; 02091 long endtime = 0; 02092 02093 starttime = (long) time(NULL); 02094 02095 while (*to && !peer) { 02096 int numlines, retry, pos = 1; 02097 struct ast_channel *watchers[AST_MAX_WATCHERS]; 02098 watchers[0] = in; 02099 start = NULL; 02100 02101 for (retry = 0; retry < 2; retry++) { 02102 numlines = 0; 02103 for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */ 02104 if (o->stillgoing) { /* Keep track of important channels */ 02105 stillgoing = 1; 02106 if (o->chan) { 02107 watchers[pos++] = o->chan; 02108 if (!start) 02109 start = o; 02110 else 02111 prev->call_next = o; 02112 prev = o; 02113 } 02114 } 02115 numlines++; 02116 } 02117 if (pos > 1 /* found */ || !stillgoing /* nobody listening */ || 02118 (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */) 02119 break; 02120 /* On "ringall" strategy we only move to the next penalty level 02121 when *all* ringing phones are done in the current penalty level */ 02122 ring_one(qe, outgoing, &numbusies); 02123 /* and retry... */ 02124 } 02125 if (pos == 1 /* not found */) { 02126 if (numlines == (numbusies + numnochan)) { 02127 ast_log(LOG_DEBUG, "Everyone is busy at this time\n"); 02128 } else { 02129 ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan); 02130 } 02131 *to = 0; 02132 return NULL; 02133 } 02134 winner = ast_waitfor_n(watchers, pos, to); 02135 for (o = start; o; o = o->call_next) { 02136 if (o->stillgoing && (o->chan) && (o->chan->_state == AST_STATE_UP)) { 02137 if (!peer) { 02138 if (option_verbose > 2) 02139 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02140 peer = o; 02141 } 02142 } else if (o->chan && (o->chan == winner)) { 02143 02144 ast_copy_string(on, o->member->interface, sizeof(on)); 02145 ast_copy_string(membername, o->member->membername, sizeof(membername)); 02146 02147 if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) { 02148 if (option_verbose > 2) 02149 ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward); 02150 numnochan++; 02151 do_hang(o); 02152 winner = NULL; 02153 continue; 02154 } else if (!ast_strlen_zero(o->chan->call_forward)) { 02155 char tmpchan[256]; 02156 char *stuff; 02157 char *tech; 02158 02159 ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan)); 02160 if ((stuff = strchr(tmpchan, '/'))) { 02161 *stuff++ = '\0'; 02162 tech = tmpchan; 02163 } else { 02164 snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context); 02165 stuff = tmpchan; 02166 tech = "Local"; 02167 } 02168 /* Before processing channel, go ahead and check for forwarding */ 02169 if (option_verbose > 2) 02170 ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name); 02171 /* Setup parameters */ 02172 o->chan = ast_request(tech, in->nativeformats, stuff, &status); 02173 if (!o->chan) { 02174 ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff); 02175 o->stillgoing = 0; 02176 numnochan++; 02177 } else { 02178 ast_channel_inherit_variables(in, o->chan); 02179 ast_channel_datastore_inherit(in, o->chan); 02180 if (o->chan->cid.cid_num) 02181 free(o->chan->cid.cid_num); 02182 o->chan->cid.cid_num = ast_strdup(in->cid.cid_num); 02183 02184 if (o->chan->cid.cid_name) 02185 free(o->chan->cid.cid_name); 02186 o->chan->cid.cid_name = ast_strdup(in->cid.cid_name); 02187 02188 ast_string_field_set(o->chan, accountcode, in->accountcode); 02189 o->chan->cdrflags = in->cdrflags; 02190 02191 if (in->cid.cid_ani) { 02192 if (o->chan->cid.cid_ani) 02193 free(o->chan->cid.cid_ani); 02194 o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani); 02195 } 02196 if (o->chan->cid.cid_rdnis) 02197 free(o->chan->cid.cid_rdnis); 02198 o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten)); 02199 if (ast_call(o->chan, tmpchan, 0)) { 02200 ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan); 02201 do_hang(o); 02202 numnochan++; 02203 } 02204 } 02205 /* Hangup the original channel now, in case we needed it */ 02206 ast_hangup(winner); 02207 continue; 02208 } 02209 f = ast_read(winner); 02210 if (f) { 02211 if (f->frametype == AST_FRAME_CONTROL) { 02212 switch (f->subclass) { 02213 case AST_CONTROL_ANSWER: 02214 /* This is our guy if someone answered. */ 02215 if (!peer) { 02216 if (option_verbose > 2) 02217 ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name); 02218 peer = o; 02219 } 02220 break; 02221 case AST_CONTROL_BUSY: 02222 if (option_verbose > 2) 02223 ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name); 02224 if (in->cdr) 02225 ast_cdr_busy(in->cdr); 02226 do_hang(o); 02227 endtime = (long)time(NULL); 02228 endtime -= starttime; 02229 rna(endtime*1000, qe, on, membername); 02230 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02231 if (qe->parent->timeoutrestart) 02232 *to = orig; 02233 ring_one(qe, outgoing, &numbusies); 02234 } 02235 numbusies++; 02236 break; 02237 case AST_CONTROL_CONGESTION: 02238 if (option_verbose > 2) 02239 ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name); 02240 if (in->cdr) 02241 ast_cdr_busy(in->cdr); 02242 endtime = (long)time(NULL); 02243 endtime -= starttime; 02244 rna(endtime*1000, qe, on, membername); 02245 do_hang(o); 02246 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02247 if (qe->parent->timeoutrestart) 02248 *to = orig; 02249 ring_one(qe, outgoing, &numbusies); 02250 } 02251 numbusies++; 02252 break; 02253 case AST_CONTROL_RINGING: 02254 if (option_verbose > 2) 02255 ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name); 02256 break; 02257 case AST_CONTROL_OFFHOOK: 02258 /* Ignore going off hook */ 02259 break; 02260 default: 02261 ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass); 02262 } 02263 } 02264 ast_frfree(f); 02265 } else { 02266 endtime = (long) time(NULL) - starttime; 02267 rna(endtime * 1000, qe, on, membername); 02268 do_hang(o); 02269 if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) { 02270 if (qe->parent->timeoutrestart) 02271 *to = orig; 02272 ring_one(qe, outgoing, &numbusies); 02273 } 02274 } 02275 } 02276 } 02277 if (winner == in) { 02278 f = ast_read(in); 02279 if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) { 02280 /* Got hung up */ 02281 *to = -1; 02282 if (f) 02283 ast_frfree(f); 02284 return NULL; 02285 } 02286 if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) { 02287 if (option_verbose > 3) 02288 ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass); 02289 *to = 0; 02290 ast_frfree(f); 02291 return NULL; 02292 } 02293 if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) { 02294 if (option_verbose > 3) 02295 ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass); 02296 *to = 0; 02297 *digit = f->subclass; 02298 ast_frfree(f); 02299 return NULL; 02300 } 02301 ast_frfree(f); 02302 } 02303 if (!*to) { 02304 for (o = start; o; o = o->call_next) 02305 rna(orig, qe, o->interface, o->member->membername); 02306 } 02307 } 02308 02309 return peer; 02310 }
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 2405 of file app_queue.c.
References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, 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().
02406 { 02407 int res = 0; 02408 02409 /* This is the holding pen for callers 2 through maxlen */ 02410 for (;;) { 02411 enum queue_member_status stat; 02412 02413 if (is_our_turn(qe)) 02414 break; 02415 02416 /* If we have timed out, break out */ 02417 if (qe->expire && (time(NULL) > qe->expire)) { 02418 *reason = QUEUE_TIMEOUT; 02419 break; 02420 } 02421 02422 stat = get_member_status(qe->parent, qe->max_penalty); 02423 02424 /* leave the queue if no agents, if enabled */ 02425 if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) { 02426 *reason = QUEUE_LEAVEEMPTY; 02427 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02428 leave_queue(qe); 02429 break; 02430 } 02431 02432 /* leave the queue if no reachable agents, if enabled */ 02433 if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) { 02434 *reason = QUEUE_LEAVEUNAVAIL; 02435 ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start); 02436 leave_queue(qe); 02437 break; 02438 } 02439 02440 /* Make a position announcement, if enabled */ 02441 if (qe->parent->announcefrequency && !ringing && 02442 (res = say_position(qe))) 02443 break; 02444 02445 /* Make a periodic announcement, if enabled */ 02446 if (qe->parent->periodicannouncefrequency && !ringing && 02447 (res = say_periodic_announcement(qe))) 02448 break; 02449 02450 /* Wait a second before checking again */ 02451 if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) { 02452 if (res > 0 && !valid_exit(qe, res)) 02453 res = 0; 02454 else 02455 break; 02456 } 02457 } 02458 02459 return res; 02460 }
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 251 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 253 of file app_queue.c.
char* app_ql_synopsis = "Writes to the queue_log" [static] |
Definition at line 252 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 236 of file app_queue.c.
char* app_upqm_descrip [static] |
Definition at line 238 of file app_queue.c.
char* app_upqm_synopsis = "Unpauses a queue member" [static] |
Definition at line 237 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 4834 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 4839 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 4829 of file app_queue.c.
Condition for the state change queue
Definition at line 682 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 |
Definition at line 286 of file app_queue.c.
Referenced by _sip_show_peers(), aMYSQL_clear(), and aMYSQL_disconnect().
Lock for the state change queue
Definition at line 680 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 4823 of file app_queue.c.
char qrm_cmd_usage[] [static] |
Initial value:
"Usage: queue remove member <channel> from <queue>\n"
Definition at line 4826 of file app_queue.c.
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 4819 of file app_queue.c.
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 676 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 287 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 678 of file app_queue.c.
int use_weight = 0 [static] |