#include "asterisk.h"
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <netinet/in.h>
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/module.h"
#include "asterisk/translate.h"
#include "asterisk/say.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/cli.h"
#include "asterisk/manager.h"
#include "asterisk/config.h"
#include "asterisk/monitor.h"
#include "asterisk/utils.h"
#include "asterisk/causes.h"
#include "asterisk/astdb.h"
#include "asterisk/devicestate.h"
#include "asterisk/stringfields.h"
#include "asterisk/astobj2.h"
#include "asterisk/global_datastores.h"

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