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