Fri Mar 8 17:30:01 2019

Asterisk developer's documentation


app_queue.c File Reference

True call queues with optional send URL on answer. More...

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

Go to the source code of this file.

Data Structures

struct  call_queue
struct  callattempt
 We define a custom "local user" structure because we use it not only for keeping track of what is in use but also for keeping track of who we're dialing. More...
struct  interfaces
struct  member
struct  member_count
struct  member_interface
struct  queue_ent
struct  queue_transfer_ds
struct  queues
struct  statechange
struct  strategy

Defines

#define ANNOUNCEHOLDTIME_ALWAYS   1
#define ANNOUNCEHOLDTIME_ONCE   2
#define AST_MAX_WATCHERS   256
#define DEFAULT_RETRY   5
#define DEFAULT_TIMEOUT   15
#define MAX_PERIODIC_ANNOUNCEMENTS   10
#define PM_MAX_LEN   8192
#define QUEUE_EMPTY_NORMAL   1
#define QUEUE_EMPTY_STRICT   2
#define QUEUE_EVENT_VARIABLES   3
#define RECHECK   1
#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumerations

enum  {
  QUEUE_STRATEGY_RINGALL = 0, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_FEWESTCALLS,
  QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RRORDERED
}
enum  qmc_status {
  QMC_VALID = 0, QMC_PAUSED, QMC_ACTIVE, QMC_FREE,
  QMC_ALL
}
enum  queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6, QUEUE_CONTINUE = 7
}

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_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, void *data)
static int attended_transfer_occurred (struct ast_channel *chan)
 mechanism to tell if a queue caller was atxferred by a queue member.
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
static void clear_and_free_interfaces (void)
static void clear_queue (struct call_queue *q)
static int cli_queue_member_count (int fd, int argc, char **argv)
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_member_count (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static struct membercreate_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 callattemptfind_best (struct callattempt *outgoing)
 find the entry with the best metric, or NULL
static struct call_queuefind_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 memberinterface_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_queueload_realtime_queue (const char *queuename)
static int manager_add_queue_member (struct mansession *s, const struct message *m)
static int manager_pause_queue_member (struct mansession *s, const struct message *m)
static int manager_queue_member_count (struct mansession *s, const struct message *m)
static int manager_queues_show (struct mansession *s, const struct message *m)
static int manager_queues_status (struct mansession *s, const struct message *m)
static int manager_remove_queue_member (struct mansession *s, const struct message *m)
static int member_cmp_fn (void *obj1, void *obj2, int flags)
static int member_hash_fn (const void *obj, const int flags)
static void monjoin_dep_warning (void)
static int num_available_members (struct call_queue *q)
 Get the number of members available to accept a call.
static int play_file (struct ast_channel *chan, char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
static int ql_exec (struct ast_channel *chan, void *data)
static int qmc_handler (const char *queuename, char *buffer, int len)
static int queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
static int queue_function_exists (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuemembercount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberlist (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberpaused (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberstatus (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_member_count (const char *qname, struct member_count *qmc)
static void queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
 Configure a queue parameter.
static int queue_show (int fd, int argc, char **argv)
static void queue_transfer_destroy (void *data)
static void queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 Log an attended transfer when a queue caller channel is masqueraded.
static void recalc_holdtime (struct queue_ent *qe, int newholdtime)
static void record_abandoned (struct queue_ent *qe)
static int reload (void)
static void reload_queue_members (void)
static int reload_queues (void)
static int remove_from_interfaces (const char *interface)
static int remove_from_queue (const char *queuename, const char *interface)
static void remove_queue (struct call_queue *q)
 removes a call_queue from the list of call_queues
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
 Part 2 of ring_one.
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member.
static void rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
 RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
static int rqm_exec (struct ast_channel *chan, void *data)
static void rr_dep_warning (void)
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
static int say_periodic_announcement (struct queue_ent *qe)
static int say_position (struct queue_ent *qe)
static int set_member_paused (const char *queuename, const char *interface, int paused)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
 sets the QUEUESTATUS channel variable
static struct ast_datastoresetup_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_lin (struct queue_ent *qe, struct callattempt *outgoing)
static int store_next_rr (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 callattemptwait_for_answer (struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
 Wait for a member to answer the call.
static int wait_our_turn (struct queue_ent *qe, int ringing, enum queue_result *reason)
 The waiting areas for callers who are not actively calling members.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, }
static char * app = "Queue"
static char * app_aqm = "AddQueueMember"
static char * app_aqm_descrip
static char * app_aqm_synopsis = "Dynamically adds queue members"
static char * app_pqm = "PauseQueueMember"
static char * app_pqm_descrip
static char * app_pqm_synopsis = "Pauses a queue member"
static char * app_ql = "QueueLog"
static char * app_ql_descrip
static char * app_ql_synopsis = "Writes to the queue_log"
static char * app_rqm = "RemoveQueueMember"
static char * app_rqm_descrip
static char * app_rqm_synopsis = "Dynamically removes queue members"
static char * app_upqm = "UnpauseQueueMember"
static char * app_upqm_descrip
static char * app_upqm_synopsis = "Unpauses a queue member"
static const struct
ast_module_info
ast_module_info = &__mod_info
static int autofill_default = 0
 queues.conf [general] option
static struct ast_cli_entry cli_add_queue_member_deprecated
static struct ast_cli_entry cli_queue []
static struct ast_cli_entry cli_remove_queue_member_deprecated
static struct ast_cli_entry cli_show_queue_deprecated
static char * descrip
struct {
   ast_cond_t   cond
   ast_mutex_t   lock
   struct {
      struct statechange *   first
      struct statechange *   last
   }   state_change_q
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static char qam_cmd_usage []
static char qmc_cmd_usage []
static char qrm_cmd_usage []
static int queue_debug = 0
 queues.conf [general] extra debug option
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static char queue_show_usage []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queueagentcount_function
static struct ast_custom_function queueexists_function
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuememberpaused_function
static struct ast_custom_function queuememberstatus_function
static struct ast_custom_function queuewaitingcount_function
static int shared_lastcall = 0
 queues.conf [general] option
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

Detailed Description

True call queues with optional send URL on answer.

Author:
Mark Spencer <markster@digium.com>
Development notes
Note:
2004-11-25: Persistent Dynamic Members added by: NetNation Communications (www.netnation.com) Kevin Lindsay <kevinl@netnation.com>

Each dynamic agent in each queue is now stored in the astdb. When asterisk is restarted, each agent will be automatically readded into their recorded queues. This feature can be configured with the 'persistent_members=<1|0>' setting in the '[general]' category in queues.conf. The default is on.

Note:
2004-06-04: Priorities in queues added by inAccess Networks (work funded by Hellas On Line (HOL) www.hol.gr).
These features added by David C. Troy <dave@toad.net>:
  • Per-queue holdtime calculation
  • Estimated holdtime announcement
  • Position announcement
  • Abandoned/completed call counters
  • Failout timer passed as optional app parameter
  • Optional monitoring of calls, started when call is answered

Patch Version 1.07 2003-12-24 01

Added servicelevel statistic by Michiel Betel <michiel@betel.nl> Added Priority jumping code for adding and removing queue members by Jonathan Stanton <asterisk@doilooklikeicare.com>

Fixed to work with CVS as of 2004-02-25 and released as 1.07a by Matthew Enger <m.enger@xi.com.au>

Definition in file app_queue.c.


Define Documentation

#define ANNOUNCEHOLDTIME_ALWAYS   1

Definition at line 406 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 407 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2318 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 141 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 142 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 144 of file app_queue.c.

Referenced by init_queue(), queue_set_param(), and say_periodic_announcement().

#define PM_MAX_LEN   8192

Definition at line 282 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 404 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 405 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 408 of file app_queue.c.

Referenced by queue_set_param(), ring_entry(), and try_calling().

#define RECHECK   1

Definition at line 143 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)
#define RES_NOSUCHQUEUE   (-3)
#define RES_NOT_DYNAMIC   (-4)
#define RES_OKAY   0
#define RES_OUTOFMEMORY   (-2)

Enumeration Type Documentation

anonymous enum
Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 
QUEUE_STRATEGY_LINEAR 
QUEUE_STRATEGY_RRORDERED 

Definition at line 116 of file app_queue.c.

enum qmc_status
Enumerator:
QMC_VALID 
QMC_PAUSED 
QMC_ACTIVE 
QMC_FREE 
QMC_ALL 

Definition at line 4514 of file app_queue.c.

04514                 {
04515    QMC_VALID = 0, /* Count valid members */
04516    QMC_PAUSED,    /* Count paused members */
04517    QMC_ACTIVE,    /* Count active members */
04518    QMC_FREE,      /* Count free members */
04519    QMC_ALL        /* Count all queue members */
04520 };

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 584 of file app_queue.c.

00584                          {
00585    QUEUE_NO_MEMBERS,
00586    QUEUE_NO_REACHABLE_MEMBERS,
00587    QUEUE_NORMAL
00588 };

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 
QUEUE_CONTINUE 

Definition at line 302 of file app_queue.c.

00302                   {
00303    QUEUE_UNKNOWN = 0,
00304    QUEUE_TIMEOUT = 1,
00305    QUEUE_JOINEMPTY = 2,
00306    QUEUE_LEAVEEMPTY = 3,
00307    QUEUE_JOINUNAVAIL = 4,
00308    QUEUE_LEAVEUNAVAIL = 5,
00309    QUEUE_FULL = 6,
00310    QUEUE_CONTINUE = 7,
00311 };


Function Documentation

static int __queues_show ( struct mansession s,
int  manager,
int  fd,
int  argc,
char **  argv 
) [static]

Definition at line 5126 of file app_queue.c.

References ao2_container_count(), ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_build_string(), ast_category_browse(), ast_check_realtime(), ast_cli(), ast_config_destroy(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime_multientry(), ast_strlen_zero(), astman_append(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, call_queue::count, devstate2str(), member::dynamic, call_queue::head, call_queue::holdtime, int2strat(), member::interface, member::lastcall, load_realtime_queue(), call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::prio, queue_show(), member::realtime, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::ringlimit, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

05127 {
05128    struct call_queue *q;
05129    struct queue_ent *qe;
05130    struct member *mem;
05131    int pos, queue_show;
05132    time_t now;
05133    char max_buf[150];
05134    char *max;
05135    size_t max_left;
05136    float sl = 0;
05137    char *term = manager ? "\r\n" : "\n";
05138    struct ao2_iterator mem_iter;
05139 
05140    time(&now);
05141    if (argc == 2)
05142       queue_show = 0;
05143    else if (argc == 3)
05144       queue_show = 1;
05145    else
05146       return RESULT_SHOWUSAGE;
05147 
05148    /* We only want to load realtime queues when a specific queue is asked for. */
05149    if (queue_show) {
05150       load_realtime_queue(argv[2]);
05151    } else if (ast_check_realtime("queues")) {
05152       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
05153       char *queuename;
05154       if (cfg) {
05155          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
05156             load_realtime_queue(queuename);
05157          }
05158          ast_config_destroy(cfg);
05159       }
05160    }
05161 
05162    AST_LIST_LOCK(&queues);
05163    if (AST_LIST_EMPTY(&queues)) {
05164       AST_LIST_UNLOCK(&queues);
05165       if (queue_show) {
05166          if (s)
05167             astman_append(s, "No such queue: %s.%s",argv[2], term);
05168          else
05169             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
05170       } else {
05171          if (s)
05172             astman_append(s, "No queues.%s", term);
05173          else
05174             ast_cli(fd, "No queues.%s", term);
05175       }
05176       return RESULT_SUCCESS;
05177    }
05178    AST_LIST_TRAVERSE(&queues, q, list) {
05179       ao2_lock(q);
05180       if (queue_show) {
05181          if (strcasecmp(q->name, argv[2]) != 0) {
05182             ao2_unlock(q);
05183             if (!AST_LIST_NEXT(q, list)) {
05184                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
05185                break;
05186             }
05187             continue;
05188          }
05189       }
05190       max_buf[0] = '\0';
05191       max = max_buf;
05192       max_left = sizeof(max_buf);
05193       if (q->maxlen)
05194          ast_build_string(&max, &max_left, "%d", q->maxlen);
05195       else
05196          ast_build_string(&max, &max_left, "unlimited");
05197       sl = 0;
05198       if (q->callscompleted > 0)
05199          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
05200       if (s)
05201          astman_append(s, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
05202                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
05203                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
05204       else
05205          ast_cli(fd, "%-12.12s has %d calls (max %s) in '%s' strategy (%ds holdtime), R:%d, W:%d, C:%d, A:%d, SL:%2.1f%% within %ds%s",
05206                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
05207                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
05208       if (ao2_container_count(q->members)) {
05209          if (s)
05210             astman_append(s, "   Members: %s", term);
05211          else
05212             ast_cli(fd, "   Members: %s", term);
05213          mem_iter = ao2_iterator_init(q->members, 0);
05214          while ((mem = ao2_iterator_next(&mem_iter))) {
05215             max_buf[0] = '\0';
05216             max = max_buf;
05217             max_left = sizeof(max_buf);
05218             if (strcasecmp(mem->membername, mem->interface)) {
05219                ast_build_string(&max, &max_left, " (%s)", mem->interface);
05220             }
05221             if (mem->penalty)
05222                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
05223             if (mem->dynamic)
05224                ast_build_string(&max, &max_left, " (dynamic)");
05225             if (mem->realtime)
05226                ast_build_string(&max, &max_left, " (realtime)");
05227             if (mem->paused)
05228                ast_build_string(&max, &max_left, " (paused)");
05229             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
05230             if (mem->calls) {
05231                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
05232                   mem->calls, (long) (time(NULL) - mem->lastcall));
05233             } else
05234                ast_build_string(&max, &max_left, " has taken no calls yet");
05235             if (s)
05236                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
05237             else
05238                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
05239             ao2_ref(mem, -1);
05240          }
05241          ao2_iterator_destroy(&mem_iter);
05242       } else if (s)
05243          astman_append(s, "   No Members%s", term);
05244       else  
05245          ast_cli(fd, "   No Members%s", term);
05246       if (q->head) {
05247          pos = 1;
05248          if (s)
05249             astman_append(s, "   Callers: %s", term);
05250          else
05251             ast_cli(fd, "   Callers: %s", term);
05252          for (qe = q->head; qe; qe = qe->next) {
05253             if (s)
05254                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
05255                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
05256                   (long) (now - qe->start) % 60, qe->prio, term);
05257             else
05258                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
05259                   qe->chan->name, (long) (now - qe->start) / 60,
05260                   (long) (now - qe->start) % 60, qe->prio, term);
05261          }
05262       } else if (s)
05263          astman_append(s, "   No Callers%s", term);
05264       else
05265          ast_cli(fd, "   No Callers%s", term);
05266       if (s)
05267          astman_append(s, "%s", term);
05268       else
05269          ast_cli(fd, "%s", term);
05270       ao2_unlock(q);
05271       if (queue_show)
05272          break;
05273    }
05274    AST_LIST_UNLOCK(&queues);
05275    return RESULT_SUCCESS;
05276 }

static void __reg_module ( void   )  [static]

Definition at line 5960 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5960 of file app_queue.c.

static int add_to_interfaces ( const char *  interface  )  [static]

Definition at line 945 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().

00946 {
00947    struct member_interface *curint;
00948 
00949    AST_LIST_LOCK(&interfaces);
00950    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00951       if (!strcasecmp(curint->interface, interface))
00952          break;
00953    }
00954 
00955    if (curint) {
00956       AST_LIST_UNLOCK(&interfaces);
00957       return 0;
00958    }
00959 
00960    if (option_debug)
00961       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00962    
00963    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00964       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00965       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00966    }
00967    AST_LIST_UNLOCK(&interfaces);
00968 
00969    return 0;
00970 }

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 3730 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().

03731 {
03732    struct call_queue *q;
03733    struct member *new_member, *old_member;
03734    int res = RES_NOSUCHQUEUE;
03735 
03736    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03737     * short-circuits if the queue is already in memory. */
03738    if (!(q = load_realtime_queue(queuename)))
03739       return res;
03740 
03741    AST_LIST_LOCK(&queues);
03742 
03743    ao2_lock(q);
03744    if ((old_member = interface_exists(q, interface)) == NULL) {
03745       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03746          add_to_interfaces(new_member->state_interface);
03747          new_member->dynamic = 1;
03748          ao2_link(q->members, new_member);
03749          q->membercount++;
03750          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03751             "Queue: %s\r\n"
03752             "Location: %s\r\n"
03753             "MemberName: %s\r\n"
03754             "Membership: %s\r\n"
03755             "Penalty: %d\r\n"
03756             "CallsTaken: %d\r\n"
03757             "LastCall: %d\r\n"
03758             "Status: %d\r\n"
03759             "Paused: %d\r\n",
03760             q->name, new_member->interface, new_member->membername,
03761             "dynamic",
03762             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03763             new_member->status, new_member->paused);
03764          
03765          ao2_ref(new_member, -1);
03766          new_member = NULL;
03767 
03768          if (dump)
03769             dump_queue_members(q);
03770          
03771          res = RES_OKAY;
03772       } else {
03773          res = RES_OUTOFMEMORY;
03774       }
03775    } else {
03776       ao2_ref(old_member, -1);
03777       res = RES_EXISTS;
03778    }
03779    ao2_unlock(q);
03780    AST_LIST_UNLOCK(&queues);
03781 
03782    return res;
03783 }

static struct call_queue* alloc_queue ( const char *  queuename  )  [static, read]

Definition at line 842 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().

00843 {
00844    struct call_queue *q;
00845 
00846    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
00847       ast_copy_string(q->name, queuename, sizeof(q->name));
00848    }
00849    return q;
00850 }

static int aqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 4111 of file app_queue.c.

References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_ERROR, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

04112 {
04113    int res=-1;
04114    struct ast_module_user *lu;
04115    char *parse, *temppos = NULL;
04116    int priority_jump = 0;
04117    AST_DECLARE_APP_ARGS(args,
04118       AST_APP_ARG(queuename);
04119       AST_APP_ARG(interface);
04120       AST_APP_ARG(penalty);
04121       AST_APP_ARG(options);
04122       AST_APP_ARG(membername);
04123       AST_APP_ARG(state_interface);
04124    );
04125    int penalty = 0;
04126 
04127    if (ast_strlen_zero(data)) {
04128       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
04129       return -1;
04130    }
04131 
04132    parse = ast_strdupa(data);
04133 
04134    AST_STANDARD_APP_ARGS(args, parse);
04135 
04136    lu = ast_module_user_add(chan);
04137 
04138    if (ast_strlen_zero(args.interface)) {
04139       args.interface = ast_strdupa(chan->name);
04140       temppos = strrchr(args.interface, '-');
04141       if (temppos)
04142          *temppos = '\0';
04143    }
04144 
04145    if (!ast_strlen_zero(args.penalty)) {
04146       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04147          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04148          penalty = 0;
04149       }
04150    }
04151    
04152    if (args.options) {
04153       if (strchr(args.options, 'j'))
04154          priority_jump = 1;
04155    }
04156 
04157    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04158    case RES_OKAY:
04159       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04160       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04161       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04162       res = 0;
04163       break;
04164    case RES_EXISTS:
04165       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04166       if (priority_jump || ast_opt_priority_jumping)
04167          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04168       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04169       res = 0;
04170       break;
04171    case RES_NOSUCHQUEUE:
04172       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04173       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04174       res = 0;
04175       break;
04176    case RES_OUTOFMEMORY:
04177       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04178       break;
04179    }
04180 
04181    ast_module_user_remove(lu);
04182 
04183    return res;
04184 }

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.

Note:
Only call this with chan locked

Definition at line 2920 of file app_queue.c.

References ast_channel_datastore_find(), and queue_transfer_info.

Referenced by try_calling().

02921 {
02922    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02923 }

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 2786 of file app_queue.c.

References ast_log(), ast_random(), member::calls, member::interface, member::lastcall, queue_ent::linpos, queue_ent::linwrapped, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, callattempt::metric, option_debug, member::penalty, QUEUE_STRATEGY_FEWESTCALLS, QUEUE_STRATEGY_LEASTRECENT, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_RANDOM, QUEUE_STRATEGY_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, member::ringcount, call_queue::ringlimit, call_queue::rrpos, call_queue::strategy, and call_queue::wrapped.

Referenced by try_calling().

02787 {
02788    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02789       return -1;
02790 
02791    switch (q->strategy) {
02792    case QUEUE_STRATEGY_RINGALL:
02793       /* Everyone equal, except for penalty */
02794       tmp->metric = mem->penalty * 1000000;
02795       break;
02796    case QUEUE_STRATEGY_ROUNDROBIN:
02797       if (!pos) {
02798          if (!q->wrapped) {
02799             /* No more channels, start over */
02800             q->rrpos = 0;
02801          } else {
02802             /* Prioritize next entry */
02803             q->rrpos++;
02804          }
02805          q->wrapped = 0;
02806       }
02807       /* Fall through */
02808    case QUEUE_STRATEGY_RRORDERED:
02809    case QUEUE_STRATEGY_RRMEMORY:
02810       if (pos < q->rrpos) {
02811          tmp->metric = 1000 + pos;
02812       } else {
02813          if (pos > q->rrpos)
02814             /* Indicate there is another priority */
02815             q->wrapped = 1;
02816          tmp->metric = pos;
02817       }
02818       tmp->metric += mem->penalty * 1000000;
02819       break;
02820    case QUEUE_STRATEGY_LINEAR:
02821       if (pos < qe->linpos) {
02822          tmp->metric = 1000 + pos;
02823       } else {
02824          if (pos > qe->linpos)
02825             /* Indicate there is another priority */
02826             qe->linwrapped = 1;
02827          tmp->metric = pos;
02828       }
02829       tmp->metric += mem->penalty * 1000000;
02830       break;
02831    case QUEUE_STRATEGY_RANDOM:
02832       tmp->metric = ast_random() % 1000;
02833       tmp->metric += mem->penalty * 1000000;
02834       break;
02835    case QUEUE_STRATEGY_FEWESTCALLS:
02836       tmp->metric = mem->calls;
02837       tmp->metric += mem->penalty * 1000000;
02838       break;
02839    case QUEUE_STRATEGY_LEASTRECENT:
02840       if (!mem->lastcall)
02841          tmp->metric = 0;
02842       else
02843          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02844       tmp->metric += mem->penalty * 1000000;
02845       break;
02846    default:
02847       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02848       break;
02849    }
02850    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02851       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02852    }
02853    if (option_debug)
02854       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02855                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02856    return 0;
02857 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1024 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

01025 {
01026    struct member_interface *curint;
01027 
01028    AST_LIST_LOCK(&interfaces);
01029    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01030       free(curint);
01031    AST_LIST_UNLOCK(&interfaces);
01032 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 936 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().

00937 {
00938    q->holdtime = 0;
00939    q->callscompleted = 0;
00940    q->callsabandoned = 0;
00941    q->callscompletedinsl = 0;
00942    q->wrapuptime = 0;
00943 }

static int cli_queue_member_count ( int  fd,
int  argc,
char **  argv 
) [static]

Definition at line 5781 of file app_queue.c.

References ast_cli(), qmc_handler(), RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

05782 {
05783    char buffer[256] = "";
05784    char *queuename;
05785    
05786    if (argc != 4) {
05787       return RESULT_SHOWUSAGE;
05788    }
05789    queuename = argv[3];
05790 
05791    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05792          ast_cli(fd, 
05793                      "Member count for queue '%s'\n"
05794                      "%s\n",
05795                      queuename, buffer); 
05796          return RESULT_SUCCESS;
05797    } else {
05798          ast_cli(fd, "No such queue: '%s'\n", queuename);
05799          return RESULT_FAILURE;
05800    }
05801 }

static int compare_weight ( struct call_queue rq,
struct member member 
) [static]

Definition at line 1882 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().

01883 {
01884    struct call_queue *q;
01885    struct member *mem;
01886    int found = 0;
01887    
01888    /* &qlock and &rq->lock already set by try_calling()
01889     * to solve deadlock */
01890    AST_LIST_TRAVERSE(&queues, q, list) {
01891       if (q == rq) /* don't check myself, could deadlock */
01892          continue;
01893       ao2_lock(q);
01894       if (q->count && q->members) {
01895          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01896             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01897             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
01898                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);
01899                found = 1;
01900             }
01901             ao2_ref(mem, -1);
01902          }
01903       }
01904       ao2_unlock(q);
01905       if (found)
01906          break;
01907    }
01908    return found;
01909 }

static char* complete_queue ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5283 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strdup, and call_queue::name.

Referenced by complete_queue_add_member(), complete_queue_member_count(), complete_queue_remove_member(), and complete_queue_show().

05284 {
05285    struct call_queue *q;
05286    char *ret = NULL;
05287    int which = 0;
05288    int wordlen = strlen(word);
05289    
05290    AST_LIST_LOCK(&queues);
05291    AST_LIST_TRAVERSE(&queues, q, list) {
05292       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05293          ret = ast_strdup(q->name); 
05294          break;
05295       }
05296    }
05297    AST_LIST_UNLOCK(&queues);
05298 
05299    return ret;
05300 }

static char* complete_queue_add_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5586 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05587 {
05588    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05589    switch (pos) {
05590    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05591       return NULL;
05592    case 4:  /* only one possible match, "to" */
05593       return state == 0 ? ast_strdup("to") : NULL;
05594    case 5:  /* <queue> */
05595       return complete_queue(line, word, pos, state);
05596    case 6: /* only one possible match, "penalty" */
05597       return state == 0 ? ast_strdup("penalty") : NULL;
05598    case 7:
05599       if (state < 100) {   /* 0-99 */
05600          char *num;
05601          if ((num = ast_malloc(3))) {
05602             sprintf(num, "%d", state);
05603          }
05604          return num;
05605       } else {
05606          return NULL;
05607       }
05608    case 8: /* only one possible match, "as" */
05609       return state == 0 ? ast_strdup("as") : NULL;
05610    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05611       return NULL;
05612    case 10:
05613       return state == 0 ? ast_strdup("state_interface") : NULL;
05614    default:
05615       return NULL;
05616    }
05617 }

static char* complete_queue_member_count ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5808 of file app_queue.c.

References complete_queue().

05809 {
05810       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05811       switch (pos) {
05812       case 3:  /* <queue> */
05813             return complete_queue(line, word, pos, state);
05814       default:
05815          return NULL;
05816       }
05817 }

static char* complete_queue_remove_member ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5654 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.

05655 {
05656    int which = 0;
05657    struct call_queue *q;
05658    struct member *m;
05659    struct ao2_iterator mem_iter;
05660 
05661    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05662    if (pos > 5 || pos < 3)
05663       return NULL;
05664    if (pos == 4)  /* only one possible match, 'from' */
05665       return state == 0 ? ast_strdup("from") : NULL;
05666 
05667    if (pos == 5)  /* No need to duplicate code */
05668       return complete_queue(line, word, pos, state);
05669 
05670    /* here is the case for 3, <member> */
05671    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05672       AST_LIST_TRAVERSE(&queues, q, list) {
05673          ao2_lock(q);
05674          mem_iter = ao2_iterator_init(q->members, 0);
05675          while ((m = ao2_iterator_next(&mem_iter))) {
05676             if (++which > state) {
05677                char *tmp;
05678                ao2_iterator_destroy(&mem_iter);
05679                ao2_unlock(q);
05680                tmp = ast_strdup(m->interface);
05681                ao2_ref(m, -1);
05682                return tmp;
05683             }
05684             ao2_ref(m, -1);
05685          }
05686          ao2_iterator_destroy(&mem_iter);
05687          ao2_unlock(q);
05688       }
05689    }
05690 
05691    return NULL;
05692 }

static char* complete_queue_show ( const char *  line,
const char *  word,
int  pos,
int  state 
) [static]

Definition at line 5302 of file app_queue.c.

References complete_queue().

05303 {
05304    if (pos == 2)
05305       return complete_queue(line, word, pos, state);
05306    return NULL;
05307 }

static int compress_char ( const char  c  )  [static]

Definition at line 852 of file app_queue.c.

Referenced by member_hash_fn().

00853 {
00854    if (c < 32)
00855       return 0;
00856    else if (c > 96)
00857       return c - 64;
00858    else
00859       return c - 32;
00860 }

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 817 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().

00818 {
00819    struct member *cur;
00820    
00821    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00822       cur->penalty = penalty;
00823       cur->paused = paused;
00824       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00825       if (!ast_strlen_zero(state_interface)) {
00826          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00827       } else {
00828          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00829       }
00830       if (!ast_strlen_zero(membername))
00831          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00832       else
00833          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00834       if (!strchr(cur->interface, '/'))
00835          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00836       cur->status = ast_device_state(cur->state_interface);
00837    }
00838 
00839    return cur;
00840 }

static void destroy_queue ( void *  obj  )  [static]

Definition at line 550 of file app_queue.c.

References ao2_ref(), free_members(), and call_queue::members.

Referenced by alloc_queue().

00551 {
00552    struct call_queue *q = obj;
00553    if (q->members) {
00554       free_members(q, 1);
00555       ao2_ref(q->members, -1);
00556    }
00557 }

static void* device_state_thread ( void *  data  )  [static]

Consumer of the statechange queue.

Definition at line 765 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().

00766 {
00767    struct statechange *sc = NULL;
00768 
00769    while (!device_state.stop) {
00770       ast_mutex_lock(&device_state.lock);
00771       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00772          ast_cond_wait(&device_state.cond, &device_state.lock);
00773          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00774       }
00775       ast_mutex_unlock(&device_state.lock);
00776 
00777       /* Check to see if we were woken up to see the request to stop */
00778       if (device_state.stop)
00779          break;
00780 
00781       if (!sc)
00782          continue;
00783 
00784       handle_statechange(sc);
00785 
00786       free(sc);
00787       sc = NULL;
00788    }
00789 
00790    if (sc)
00791       free(sc);
00792 
00793    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00794       free(sc);
00795 
00796    return NULL;
00797 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1912 of file app_queue.c.

References ast_hangup(), callattempt::chan, and callattempt::stillgoing.

Referenced by ring_entry(), and wait_for_answer().

01913 {
01914    o->stillgoing = 0;
01915    ast_hangup(o->chan);
01916    o->chan = NULL;
01917 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 3638 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().

03639 {
03640    struct member *cur_member;
03641    char value[PM_MAX_LEN];
03642    int value_len = 0;
03643    int res;
03644    struct ao2_iterator mem_iter;
03645 
03646    memset(value, 0, sizeof(value));
03647 
03648    if (!pm_queue)
03649       return;
03650 
03651    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03652    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03653       if (!cur_member->dynamic) {
03654          ao2_ref(cur_member, -1);
03655          continue;
03656       }
03657 
03658       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03659          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03660 
03661       ao2_ref(cur_member, -1);
03662 
03663       if (res != strlen(value + value_len)) {
03664          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03665          break;
03666       }
03667       value_len += res;
03668    }
03669    ao2_iterator_destroy(&mem_iter);
03670    
03671    if (value_len && !cur_member) {
03672       if (ast_db_put(pm_family, pm_queue->name, value))
03673          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03674    } else
03675       /* Delete the entry if the queue is empty or there is an error */
03676       ast_db_del(pm_family, pm_queue->name);
03677 }

static struct callattempt* find_best ( struct callattempt outgoing  )  [static, read]

find the entry with the best metric, or NULL

Definition at line 2133 of file app_queue.c.

References callattempt::metric, and callattempt::q_next.

Referenced by ring_one(), store_next_lin(), and store_next_rr().

02134 {
02135    struct callattempt *best = NULL, *cur;
02136 
02137    for (cur = outgoing; cur; cur = cur->q_next) {
02138       if (cur->stillgoing &&              /* Not already done */
02139          !cur->chan &&              /* Isn't already going */
02140          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02141          best = cur;
02142       }
02143    }
02144 
02145    return best;
02146 }

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.

Returns:
Return the queue, or NULL if it doesn't exist.
Note:
Should be called with the global qlock locked.

Note:
Hmm, can't seem to distinguish a DB failure from a not found condition... So we might delete an in-core queue in case of DB failure.

Definition at line 1262 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().

01263 {
01264    struct ast_variable *v;
01265    struct call_queue *q;
01266    struct member *m;
01267    struct ao2_iterator mem_iter;
01268    char *interface = NULL;
01269    char *tmp, *tmp_name;
01270    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01271 
01272    /* Find the queue in the in-core list (we will create a new one if not found). */
01273    AST_LIST_TRAVERSE(&queues, q, list) {
01274       if (!strcasecmp(q->name, queuename))
01275          break;
01276    }
01277 
01278    /* Static queues override realtime. */
01279    if (q) {
01280       ao2_lock(q);
01281       if (!q->realtime) {
01282          if (q->dead) {
01283             ao2_unlock(q);
01284             return NULL;
01285          } else {
01286             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01287             ao2_unlock(q);
01288             return q;
01289          }
01290       }
01291    } else if (!member_config)
01292       /* Not found in the list, and it's not realtime ... */
01293       return NULL;
01294 
01295    /* Check if queue is defined in realtime. */
01296    if (!queue_vars) {
01297       /* Delete queue from in-core list if it has been deleted in realtime. */
01298       if (q) {
01299          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01300             found condition... So we might delete an in-core queue
01301             in case of DB failure. */
01302          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01303 
01304          q->dead = 1;
01305          /* Delete if unused (else will be deleted when last caller leaves). */
01306          if (!q->count) {
01307             /* Delete. */
01308             ao2_unlock(q);
01309             remove_queue(q);
01310          } else
01311             ao2_unlock(q);
01312       }
01313       return NULL;
01314    }
01315 
01316    /* Create a new queue if an in-core entry does not exist yet. */
01317    if (!q) {
01318       struct ast_variable *tmpvar;
01319       if (!(q = alloc_queue(queuename)))
01320          return NULL;
01321       ao2_lock(q);
01322       clear_queue(q);
01323       q->realtime = 1;
01324       AST_LIST_INSERT_HEAD(&queues, q, list);
01325    
01326       /* Due to the fact that the "rrordered" strategy will have a different allocation
01327        * scheme for queue members, we must devise the queue's strategy before other initializations.
01328        * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2
01329        * container used will have only a single bucket instead of the typical number.
01330        */
01331       for (tmpvar = queue_vars; tmpvar; tmpvar = tmpvar->next) {
01332          if (!strcasecmp(tmpvar->name, "strategy")) {
01333             q->strategy = strat2int(tmpvar->value);
01334             if (q->strategy < 0) {
01335                ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01336                tmpvar->value, q->name);
01337                q->strategy = QUEUE_STRATEGY_RINGALL;
01338             }
01339             break;
01340          }
01341       }
01342       /* We traversed all variables and didn't find a strategy */
01343       if (!tmpvar) {
01344          q->strategy = QUEUE_STRATEGY_RINGALL;
01345       }
01346    }
01347    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01348 
01349    memset(tmpbuf, 0, sizeof(tmpbuf));
01350    for (v = queue_vars; v; v = v->next) {
01351       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01352       if ((tmp = strchr(v->name, '_'))) {
01353          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01354          tmp_name = tmpbuf;
01355          tmp = tmp_name;
01356          while ((tmp = strchr(tmp, '_')))
01357             *tmp++ = '-';
01358       } else
01359          tmp_name = v->name;
01360 
01361       /* NULL values don't get returned from realtime; blank values should
01362        * still get set.  If someone doesn't want a value to be set, they
01363        * should set the realtime column to NULL, not blank. */
01364       queue_set_param(q, tmp_name, v->value, -1, 0);
01365    }
01366 
01367    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01368       rr_dep_warning();
01369 
01370    /* Temporarily set realtime members dead so we can detect deleted ones. 
01371     * Also set the membercount correctly for realtime*/
01372    mem_iter = ao2_iterator_init(q->members, 0);
01373    while ((m = ao2_iterator_next(&mem_iter))) {
01374       q->membercount++;
01375       if (m->realtime)
01376          m->dead = 1;
01377       ao2_ref(m, -1);
01378    }
01379    ao2_iterator_destroy(&mem_iter);
01380 
01381    while ((interface = ast_category_browse(member_config, interface))) {
01382       rt_handle_member_record(q, interface,
01383          ast_variable_retrieve(member_config, interface, "membername"),
01384          ast_variable_retrieve(member_config, interface, "penalty"),
01385          ast_variable_retrieve(member_config, interface, "paused"),
01386          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01387    }
01388 
01389    /* Delete all realtime members that have been deleted in DB. */
01390    mem_iter = ao2_iterator_init(q->members, 0);
01391    while ((m = ao2_iterator_next(&mem_iter))) {
01392       if (m->dead) {
01393          ao2_unlink(q->members, m);
01394          ao2_unlock(q);
01395          remove_from_interfaces(m->state_interface);
01396          ao2_lock(q);
01397          q->membercount--;
01398       }
01399       ao2_ref(m, -1);
01400    }
01401    ao2_iterator_destroy(&mem_iter);
01402 
01403    ao2_unlock(q);
01404 
01405    return q;
01406 }

static void free_members ( struct call_queue q,
int  all 
) [static]

Definition at line 1242 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().

01243 {
01244    /* Free non-dynamic members */
01245    struct member *cur;
01246    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01247 
01248    while ((cur = ao2_iterator_next(&mem_iter))) {
01249       if (all || !cur->dynamic) {
01250          ao2_unlink(q->members, cur);
01251          remove_from_interfaces(cur->state_interface);
01252          q->membercount--;
01253       }
01254       ao2_ref(cur, -1);
01255    }
01256    ao2_iterator_destroy(&mem_iter);
01257 }

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 596 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().

00597 {
00598    struct member *member;
00599    struct ao2_iterator mem_iter;
00600    enum queue_member_status result = QUEUE_NO_MEMBERS;
00601    int allpaused = 1, empty = 1;
00602 
00603    ao2_lock(q);
00604    mem_iter = ao2_iterator_init(q->members, 0);
00605    while ((member = ao2_iterator_next(&mem_iter))) {
00606       empty = 0;
00607 
00608       if (max_penalty && (member->penalty > max_penalty)) {
00609          ao2_ref(member, -1);
00610          continue;
00611       }
00612 
00613       if (member->paused) {
00614          ao2_ref(member, -1);
00615          continue;
00616       } else {
00617          allpaused = 0;
00618       }
00619 
00620       switch (member->status) {
00621       case AST_DEVICE_INVALID:
00622          /* nothing to do */
00623          ao2_ref(member, -1);
00624          break;
00625       case AST_DEVICE_UNAVAILABLE:
00626          result = QUEUE_NO_REACHABLE_MEMBERS;
00627          ao2_ref(member, -1);
00628          break;
00629       default:
00630          ao2_unlock(q);
00631          ao2_ref(member, -1);
00632          return QUEUE_NORMAL;
00633       }
00634    }
00635    ao2_iterator_destroy(&mem_iter);
00636    ao2_unlock(q);
00637 
00638    if (!empty && allpaused) {
00639       result = QUEUE_NO_REACHABLE_MEMBERS;
00640    }
00641    return result;
00642 }

static int handle_queue_add_member ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 5526 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.

05527 {
05528    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05529    int penalty;
05530 
05531    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05532       return RESULT_SHOWUSAGE;
05533    } else if (strcmp(argv[4], "to")) {
05534       return RESULT_SHOWUSAGE;
05535    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05536       return RESULT_SHOWUSAGE;
05537    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05538       return RESULT_SHOWUSAGE;
05539    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05540       return RESULT_SHOWUSAGE;
05541    }
05542 
05543    queuename = argv[5];
05544    interface = argv[3];
05545    if (argc >= 8) {
05546       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05547          if (penalty < 0) {
05548             ast_cli(fd, "Penalty must be >= 0\n");
05549             penalty = 0;
05550          }
05551       } else {
05552          ast_cli(fd, "Penalty must be an integer >= 0\n");
05553          penalty = 0;
05554       }
05555    } else {
05556       penalty = 0;
05557    }
05558 
05559    if (argc >= 10) {
05560       membername = argv[9];
05561    }
05562 
05563    if (argc >= 12) {
05564       state_interface = argv[11];
05565    }
05566 
05567    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05568    case RES_OKAY:
05569       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05570       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05571       return RESULT_SUCCESS;
05572    case RES_EXISTS:
05573       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05574       return RESULT_FAILURE;
05575    case RES_NOSUCHQUEUE:
05576       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05577       return RESULT_FAILURE;
05578    case RES_OUTOFMEMORY:
05579       ast_cli(fd, "Out of memory\n");
05580       return RESULT_FAILURE;
05581    default:
05582       return RESULT_FAILURE;
05583    }
05584 }

static int handle_queue_remove_member ( int  fd,
int  argc,
char *  argv[] 
) [static]

Definition at line 5619 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.

05620 {
05621    char *queuename, *interface;
05622 
05623    if (argc != 6) {
05624       return RESULT_SHOWUSAGE;
05625    } else if (strcmp(argv[4], "from")) {
05626       return RESULT_SHOWUSAGE;
05627    }
05628 
05629    queuename = argv[5];
05630    interface = argv[3];
05631 
05632    switch (remove_from_queue(queuename, interface)) {
05633    case RES_OKAY:
05634       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05635       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05636       return RESULT_SUCCESS;
05637    case RES_EXISTS:
05638       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05639       return RESULT_FAILURE;
05640    case RES_NOSUCHQUEUE:
05641       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05642       return RESULT_FAILURE;
05643    case RES_OUTOFMEMORY:
05644       ast_cli(fd, "Out of memory\n");
05645       return RESULT_FAILURE;
05646    case RES_NOT_DYNAMIC:
05647       ast_cli(fd, "Member not dynamic\n");
05648       return RESULT_FAILURE;
05649    default:
05650       return RESULT_FAILURE;
05651    }
05652 }

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 704 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().

00705 {
00706    struct member_interface *curint;
00707    char *loc;
00708    char *technology;
00709    char interface[80];
00710 
00711    technology = ast_strdupa(sc->dev);
00712    loc = strchr(technology, '/');
00713    if (loc) {
00714       *loc++ = '\0';
00715    } else {
00716       return NULL;
00717    }
00718 
00719    AST_LIST_LOCK(&interfaces);
00720    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00721       char *slash_pos;
00722       ast_copy_string(interface, curint->interface, sizeof(interface));
00723       if ((slash_pos = strchr(interface, '/')))
00724          if ((slash_pos = strchr(slash_pos + 1, '/')))
00725             *slash_pos = '\0';
00726 
00727       if (!strcasecmp(interface, sc->dev))
00728          break;
00729    }
00730    AST_LIST_UNLOCK(&interfaces);
00731 
00732    if (!curint) {
00733       if (option_debug > 2)
00734          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));
00735       return NULL;
00736    }
00737 
00738    if (option_debug)
00739       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00740 
00741    update_status(sc->dev, sc->state);
00742 
00743    return NULL;
00744 }

static void hangupcalls ( struct callattempt outgoing,
struct ast_channel exception 
) [static]

Definition at line 1812 of file app_queue.c.

References ao2_ref(), ast_hangup(), callattempt::chan, free, callattempt::member, and callattempt::q_next.

Referenced by try_calling().

01813 {
01814    struct callattempt *oo;
01815 
01816    while (outgoing) {
01817       /* Hangup any existing lines we have open */
01818       if (outgoing->chan && (outgoing->chan != exception))
01819          ast_hangup(outgoing->chan);
01820       oo = outgoing;
01821       outgoing = outgoing->q_next;
01822       if (oo->member)
01823          ao2_ref(oo->member, -1);
01824       free(oo);
01825    }
01826 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 880 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_LINEAR, QUEUE_STRATEGY_RRORDERED, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, 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().

00881 {
00882    int i;
00883 
00884    q->dead = 0;
00885    q->retry = DEFAULT_RETRY;
00886    q->timeout = -1;
00887    q->maxlen = 0;
00888    q->ringlimit = 0;
00889    q->announcefrequency = 0;
00890    q->announceholdtime = 0;
00891    q->roundingseconds = 0; /* Default - don't announce seconds */
00892    q->servicelevel = 0;
00893    q->ringinuse = 1;
00894    q->setinterfacevar = 0;
00895    q->autofill = autofill_default;
00896    q->montype = montype_default;
00897    q->moh[0] = '\0';
00898    q->announce[0] = '\0';
00899    q->context[0] = '\0';
00900    q->monfmt[0] = '\0';
00901    q->periodicannouncefrequency = 0;
00902    q->reportholdtime = 0;
00903    q->monjoin = 0;
00904    q->wrapuptime = 0;
00905    q->joinempty = 0;
00906    q->leavewhenempty = 0;
00907    q->memberdelay = 0;
00908    q->maskmemberstatus = 0;
00909    q->eventwhencalled = 0;
00910    q->weight = 0;
00911    q->timeoutrestart = 0;
00912         if (!q->members) {
00913      if (q->strategy == QUEUE_STRATEGY_LINEAR || q->strategy == QUEUE_STRATEGY_RRORDERED)
00914        /* linear strategy depends on order, so we have to place all members in a single bucket */
00915        q->members = ao2_container_alloc(1, member_hash_fn, member_cmp_fn);
00916      else
00917        q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00918         }
00919    q->membercount = 0;
00920    q->found = 1;
00921    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00922    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00923    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00924    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00925    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00926    ast_copy_string(q->sound_minute, "queue-minute", sizeof(q->sound_minute));
00927    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00928    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00929    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00930    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00931    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00932       q->sound_periodicannounce[i][0]='\0';
00933    }
00934 }

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 560 of file app_queue.c.

References ao2_ref(), call_queue::head, and queue_ent::next.

Referenced by join_queue().

00561 {
00562    struct queue_ent *cur;
00563 
00564    if (!q || !new)
00565       return;
00566    if (prev) {
00567       cur = prev->next;
00568       prev->next = new;
00569    } else {
00570       cur = q->head;
00571       q->head = new;
00572    }
00573    new->next = cur;
00574 
00575    /* every queue_ent must have a reference to it's parent call_queue, this
00576     * reference does not go away until the end of the queue_ent's life, meaning
00577     * that even when the queue_ent leaves the call_queue this ref must remain. */
00578    ao2_ref(q, +1);
00579    new->parent = q;
00580    new->pos = ++(*pos);
00581    new->opos = *pos;
00582 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 514 of file app_queue.c.

References strategy::name, and strategies.

Referenced by __queues_show().

00515 {
00516    int x;
00517 
00518    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00519       if (strategy == strategies[x].strategy)
00520          return strategies[x].name;
00521    }
00522 
00523    return "<unknown>";
00524 }

static struct member* interface_exists ( struct call_queue q,
const char *  interface 
) [static, read]

Definition at line 3611 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().

03612 {
03613    struct member *mem;
03614    struct ao2_iterator mem_iter;
03615 
03616    if (!q)
03617       return NULL;
03618 
03619    mem_iter = ao2_iterator_init(q->members, 0);
03620    while ((mem = ao2_iterator_next(&mem_iter))) {
03621       if (!strcasecmp(interface, mem->interface)) {
03622          ao2_iterator_destroy(&mem_iter);
03623          return mem;
03624       }
03625       ao2_ref(mem, -1);
03626    }
03627    ao2_iterator_destroy(&mem_iter);
03628 
03629    return NULL;
03630 }

static int interface_exists_global ( const char *  interface  )  [static]

Definition at line 972 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().

00973 {
00974    struct call_queue *q;
00975    struct member *mem;
00976    struct ao2_iterator mem_iter;
00977    int ret = 0;
00978 
00979    AST_LIST_LOCK(&queues);
00980    AST_LIST_TRAVERSE(&queues, q, list) {
00981       ao2_lock(q);
00982       mem_iter = ao2_iterator_init(q->members, 0);
00983       while ((mem = ao2_iterator_next(&mem_iter))) {
00984          if (!strcasecmp(mem->state_interface, interface)) {
00985             ao2_ref(mem, -1);
00986             ret = 1;
00987             break;
00988          }
00989          ao2_ref(mem, -1);
00990       }
00991       ao2_iterator_destroy(&mem_iter);
00992       ao2_unlock(q);
00993       if (ret)
00994          break;
00995    }
00996    AST_LIST_UNLOCK(&queues);
00997 
00998    return ret;
00999 }

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.

Parameters:
[in] qe The caller who wants to know if it is his turn
Return values:
0 It is not our turn
1 It is our turn

Definition at line 2621 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_log(), call_queue::autofill, queue_ent::chan, call_queue::head, LOG_DEBUG, queue_ent::next, num_available_members(), option_debug, queue_ent::parent, queue_ent::pending, and queue_ent::pos.

Referenced by queue_exec(), and wait_our_turn().

02622 {
02623    struct queue_ent *ch;
02624    int res;
02625    int avl;
02626    int idx = 0;
02627    /* This needs a lock. How many members are available to be served? */
02628    ao2_lock(qe->parent);
02629 
02630    avl = num_available_members(qe->parent);
02631 
02632    ch = qe->parent->head;
02633 
02634    if (option_debug) {
02635       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02636    }
02637 
02638    while ((idx < avl) && (ch) && (ch != qe)) {
02639       if (!ch->pending)
02640          idx++;
02641       ch = ch->next;       
02642    }
02643 
02644    ao2_unlock(qe->parent);
02645    /* If the queue entry is within avl [the number of available members] calls from the top ... 
02646     * Autofill and position check added to support autofill=no (as only calls
02647     * from the front of the queue are valid when autofill is disabled)
02648     */
02649    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02650       if (option_debug)
02651          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02652       res = 1;
02653    } else {
02654       if (option_debug)
02655          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02656       res = 0;
02657    }
02658 
02659    return res;
02660 }

static int join_queue ( char *  queuename,
struct queue_ent qe,
enum queue_result reason 
) [static]

Definition at line 1529 of file app_queue.c.

References call_queue::announce, queue_ent::announce, ao2_lock(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::context, queue_ent::context, call_queue::count, EVENT_FLAG_CALL, get_member_status(), call_queue::head, insert_entry(), call_queue::joinempty, load_realtime_queue(), LOG_DEBUG, manager_event(), queue_ent::max_penalty, call_queue::maxlen, call_queue::moh, queue_ent::moh, call_queue::name, queue_ent::next, option_debug, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, and S_OR.

Referenced by queue_exec().

01530 {
01531    struct call_queue *q;
01532    struct queue_ent *cur, *prev = NULL;
01533    int res = -1;
01534    int pos = 0;
01535    int inserted = 0;
01536    enum queue_member_status stat;
01537 
01538    if (!(q = load_realtime_queue(queuename)))
01539       return res;
01540 
01541    AST_LIST_LOCK(&queues);
01542    ao2_lock(q);
01543 
01544    /* This is our one */
01545    stat = get_member_status(q, qe->max_penalty);
01546    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01547       *reason = QUEUE_JOINEMPTY;
01548    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
01549       *reason = QUEUE_JOINUNAVAIL;
01550    else if (q->maxlen && (q->count >= q->maxlen))
01551       *reason = QUEUE_FULL;
01552    else {
01553       /* There's space for us, put us at the right position inside
01554        * the queue.
01555        * Take into account the priority of the calling user */
01556       inserted = 0;
01557       prev = NULL;
01558       cur = q->head;
01559       while (cur) {
01560          /* We have higher priority than the current user, enter
01561           * before him, after all the other users with priority
01562           * higher or equal to our priority. */
01563          if ((!inserted) && (qe->prio > cur->prio)) {
01564             insert_entry(q, prev, qe, &pos);
01565             inserted = 1;
01566          }
01567          cur->pos = ++pos;
01568          prev = cur;
01569          cur = cur->next;
01570       }
01571       /* No luck, join at the end of the queue */
01572       if (!inserted)
01573          insert_entry(q, prev, qe, &pos);
01574       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01575       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01576       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01577       q->count++;
01578       res = 0;
01579       manager_event(EVENT_FLAG_CALL, "Join",
01580          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01581          qe->chan->name,
01582          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01583          S_OR(qe->chan->cid.cid_name, "unknown"),
01584          q->name, qe->pos, q->count, qe->chan->uniqueid );
01585       if (option_debug)
01586          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01587    }
01588    ao2_unlock(q);
01589    AST_LIST_UNLOCK(&queues);
01590 
01591    return res;
01592 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1771 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_log(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, LOG_DEBUG, manager_event(), call_queue::name, queue_ent::next, option_debug, queue_ent::parent, queue_ent::pos, and remove_queue().

Referenced by queue_exec(), try_calling(), and wait_our_turn().

01772 {
01773    struct call_queue *q;
01774    struct queue_ent *cur, *prev = NULL;
01775    int pos = 0;
01776 
01777    if (!(q = qe->parent))
01778       return;
01779    ao2_lock(q);
01780 
01781    prev = NULL;
01782    for (cur = q->head; cur; cur = cur->next) {
01783       if (cur == qe) {
01784          q->count--;
01785 
01786          /* Take us out of the queue */
01787          manager_event(EVENT_FLAG_CALL, "Leave",
01788             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01789             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01790          if (option_debug)
01791             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01792          /* Take us out of the queue */
01793          if (prev)
01794             prev->next = cur->next;
01795          else
01796             q->head = cur->next;
01797       } else {
01798          /* Renumber the people after us in the queue based on a new count */
01799          cur->pos = ++pos;
01800          prev = cur;
01801       }
01802    }
01803    ao2_unlock(q);
01804 
01805    if (q->dead && !q->count) {   
01806       /* It's dead and nobody is in it, so kill it */
01807       remove_queue(q);
01808    }
01809 }

static int load_module ( void   )  [static]

Definition at line 5911 of file app_queue.c.

References aqm_exec(), ast_cli_register_multiple(), ast_cond_init(), ast_custom_function_register(), ast_devstate_add(), ast_manager_register, AST_MODULE_LOAD_DECLINE, ast_mutex_init(), ast_pthread_create, ast_register_application(), cli_queue, device_state, device_state_thread(), EVENT_FLAG_AGENT, manager_add_queue_member(), manager_pause_queue_member(), manager_queue_member_count(), manager_queues_show(), manager_queues_status(), manager_remove_queue_member(), pqm_exec(), ql_exec(), queue_exec(), queueagentcount_function, queueexists_function, queuemembercount_function, queuememberlist_function, queuememberpaused_function, queuememberstatus_function, queuewaitingcount_function, reload_queue_members(), reload_queues(), rqm_exec(), statechange_queue(), and upqm_exec().

05912 {
05913    int res;
05914 
05915    if (!reload_queues())
05916       return AST_MODULE_LOAD_DECLINE;
05917 
05918    if (queue_persistent_members)
05919       reload_queue_members();
05920 
05921    ast_mutex_init(&device_state.lock);
05922    ast_cond_init(&device_state.cond, NULL);
05923    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05924 
05925    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05926    res = ast_register_application(app, queue_exec, synopsis, descrip);
05927    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05928    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05929    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05930    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05931    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05932    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05933    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05934    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05935    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05936    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05937    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05938    res |= ast_custom_function_register(&queueexists_function);
05939    res |= ast_custom_function_register(&queueagentcount_function);
05940    res |= ast_custom_function_register(&queuemembercount_function);
05941    res |= ast_custom_function_register(&queuememberlist_function);
05942    res |= ast_custom_function_register(&queuememberstatus_function);
05943    res |= ast_custom_function_register(&queuememberpaused_function);
05944    res |= ast_custom_function_register(&queuewaitingcount_function);
05945    res |= ast_devstate_add(statechange_queue, NULL);
05946 
05947    return res;
05948 }

static struct call_queue* load_realtime_queue ( const char *  queuename  )  [static, read]

Note:
Load from realtime before taking the global qlock, to avoid blocking all queue operations while waiting for the DB.

This will be two separate database transactions, so we might see queue parameters as they were before another process changed the queue and member list as it was after the change. Thus we might see an empty member list when a queue is deleted. In practise, this is unlikely to cause a problem.

Definition at line 1479 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_exists(), queue_function_queuemembercount(), queue_member_count(), and reload_queue_members().

01480 {
01481    struct ast_variable *queue_vars;
01482    struct ast_config *member_config = NULL;
01483    struct call_queue *q;
01484 
01485    /* Find the queue in the in-core list first. */
01486    AST_LIST_LOCK(&queues);
01487    AST_LIST_TRAVERSE(&queues, q, list) {
01488       if (!strcasecmp(q->name, queuename)) {
01489          break;
01490       }
01491    }
01492    AST_LIST_UNLOCK(&queues);
01493 
01494    if (!q || q->realtime) {
01495       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01496          queue operations while waiting for the DB.
01497 
01498          This will be two separate database transactions, so we might
01499          see queue parameters as they were before another process
01500          changed the queue and member list as it was after the change.
01501          Thus we might see an empty member list when a queue is
01502          deleted. In practise, this is unlikely to cause a problem. */
01503 
01504       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01505       if (queue_vars) {
01506          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01507          if (!member_config) {
01508             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01509             ast_variables_destroy(queue_vars);
01510             return NULL;
01511          }
01512       }
01513 
01514       AST_LIST_LOCK(&queues);
01515 
01516       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01517       if (member_config)
01518          ast_config_destroy(member_config);
01519       if (queue_vars)
01520          ast_variables_destroy(queue_vars);
01521 
01522       AST_LIST_UNLOCK(&queues);
01523    } else { 
01524       update_realtime_members(q);
01525    }
01526    return q;
01527 }

static int manager_add_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 5418 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().

05419 {
05420    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05421    int paused, penalty = 0;
05422 
05423    queuename = astman_get_header(m, "Queue");
05424    interface = astman_get_header(m, "Interface");
05425    penalty_s = astman_get_header(m, "Penalty");
05426    paused_s = astman_get_header(m, "Paused");
05427    membername = astman_get_header(m, "MemberName");
05428    state_interface = astman_get_header(m, "StateInterface");
05429 
05430    if (ast_strlen_zero(queuename)) {
05431       astman_send_error(s, m, "'Queue' not specified.");
05432       return 0;
05433    }
05434 
05435    if (ast_strlen_zero(interface)) {
05436       astman_send_error(s, m, "'Interface' not specified.");
05437       return 0;
05438    }
05439 
05440    if (ast_strlen_zero(penalty_s))
05441       penalty = 0;
05442    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05443       penalty = 0;
05444 
05445    if (ast_strlen_zero(paused_s))
05446       paused = 0;
05447    else
05448       paused = abs(ast_true(paused_s));
05449 
05450    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05451    case RES_OKAY:
05452       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05453       astman_send_ack(s, m, "Added interface to queue");
05454       break;
05455    case RES_EXISTS:
05456       astman_send_error(s, m, "Unable to add interface: Already there");
05457       break;
05458    case RES_NOSUCHQUEUE:
05459       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05460       break;
05461    case RES_OUTOFMEMORY:
05462       astman_send_error(s, m, "Out of memory");
05463       break;
05464    }
05465 
05466    return 0;
05467 }

static int manager_pause_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 5503 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().

05504 {
05505    const char *queuename, *interface, *paused_s;
05506    int paused;
05507 
05508    interface = astman_get_header(m, "Interface");
05509    paused_s = astman_get_header(m, "Paused");
05510    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05511 
05512    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05513       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05514       return 0;
05515    }
05516 
05517    paused = abs(ast_true(paused_s));
05518 
05519    if (set_member_paused(queuename, interface, paused))
05520       astman_send_error(s, m, "Interface not found");
05521    else
05522       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05523    return 0;
05524 }

static int manager_queue_member_count ( struct mansession s,
const struct message m 
) [static]

Definition at line 5763 of file app_queue.c.

References ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), qmc_handler(), and RESULT_SUCCESS.

Referenced by load_module().

05764 {
05765    char buffer[256] = "";
05766    const char *queuename = astman_get_header(m,"Queue");
05767 
05768    if (ast_strlen_zero(queuename)) {
05769          astman_send_error(s, m, "'Queue' not specified.");
05770          return 0;
05771    }
05772    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05773          astman_send_ack(s, m, buffer);
05774          return RESULT_SUCCESS;
05775    } else {
05776          astman_send_error(s, m, "Queue not found.");
05777          return 0;
05778    }
05779 }

static int manager_queues_show ( struct mansession s,
const struct message m 
) [static]

Definition at line 5312 of file app_queue.c.

References __queues_show(), astman_append(), and RESULT_SUCCESS.

Referenced by load_module().

05313 {
05314    char *a[] = { "queue", "show" };
05315 
05316    __queues_show(s, 1, -1, 2, a);
05317    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05318 
05319    return RESULT_SUCCESS;
05320 }

static int manager_queues_status ( struct mansession s,
const struct message m 
) [static]

Definition at line 5323 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_ack(), member::calls, call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, queue_ent::chan, ast_channel::cid, ast_callerid::cid_name, ast_callerid::cid_num, call_queue::count, member::dynamic, call_queue::head, call_queue::holdtime, member::interface, member::lastcall, call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, RESULT_SUCCESS, call_queue::ringlimit, S_OR, call_queue::servicelevel, queue_ent::start, member::status, and call_queue::weight.

Referenced by load_module().

05324 {
05325    time_t now;
05326    int pos;
05327    const char *id = astman_get_header(m,"ActionID");
05328    const char *queuefilter = astman_get_header(m,"Queue");
05329    const char *memberfilter = astman_get_header(m,"Member");
05330    char idText[256] = "";
05331    struct call_queue *q;
05332    struct queue_ent *qe;
05333    float sl = 0;
05334    struct member *mem;
05335    struct ao2_iterator mem_iter;
05336 
05337    astman_send_ack(s, m, "Queue status will follow");
05338    time(&now);
05339    AST_LIST_LOCK(&queues);
05340    if (!ast_strlen_zero(id))
05341       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05342 
05343    AST_LIST_TRAVERSE(&queues, q, list) {
05344       ao2_lock(q);
05345 
05346       /* List queue properties */
05347       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05348          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05349          astman_append(s, "Event: QueueParams\r\n"
05350             "Queue: %s\r\n"
05351             "Max: %d\r\n"
05352             "Calls: %d\r\n"
05353             "Holdtime: %d\r\n"
05354             "Completed: %d\r\n"
05355             "Abandoned: %d\r\n"
05356             "ServiceLevel: %d\r\n"
05357             "ServicelevelPerf: %2.1f\r\n"
05358             "RingLimit: %d\r\n"
05359             "Weight: %d\r\n"
05360             "%s"
05361             "\r\n",
05362             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
05363             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
05364          /* List Queue Members */
05365          mem_iter = ao2_iterator_init(q->members, 0);
05366          while ((mem = ao2_iterator_next(&mem_iter))) {
05367             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
05368                astman_append(s, "Event: QueueMember\r\n"
05369                   "Queue: %s\r\n"
05370                   "Name: %s\r\n"
05371                   "Location: %s\r\n"
05372                   "Membership: %s\r\n"
05373                   "Penalty: %d\r\n"
05374                   "CallsTaken: %d\r\n"
05375                   "LastCall: %d\r\n"
05376                   "Status: %d\r\n"
05377                   "Paused: %d\r\n"
05378                   "%s"
05379                   "\r\n",
05380                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05381                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05382             }
05383             ao2_ref(mem, -1);
05384          }
05385          ao2_iterator_destroy(&mem_iter);
05386          /* List Queue Entries */
05387          pos = 1;
05388          for (qe = q->head; qe; qe = qe->next) {
05389             astman_append(s, "Event: QueueEntry\r\n"
05390                "Queue: %s\r\n"
05391                "Position: %d\r\n"
05392                "Channel: %s\r\n"
05393                "CallerID: %s\r\n"
05394                "CallerIDName: %s\r\n"
05395                "Wait: %ld\r\n"
05396                "%s"
05397                "\r\n",
05398                q->name, pos++, qe->chan->name,
05399                S_OR(qe->chan->cid.cid_num, "unknown"),
05400                S_OR(qe->chan->cid.cid_name, "unknown"),
05401                (long) (now - qe->start), idText);
05402          }
05403       }
05404       ao2_unlock(q);
05405    }
05406 
05407    astman_append(s,
05408       "Event: QueueStatusComplete\r\n"
05409       "%s"
05410       "\r\n",idText);
05411 
05412    AST_LIST_UNLOCK(&queues);
05413 
05414 
05415    return RESULT_SUCCESS;
05416 }

static int manager_remove_queue_member ( struct mansession s,
const struct message m 
) [static]

Definition at line 5469 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().

05470 {
05471    const char *queuename, *interface;
05472 
05473    queuename = astman_get_header(m, "Queue");
05474    interface = astman_get_header(m, "Interface");
05475 
05476    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05477       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05478       return 0;
05479    }
05480 
05481    switch (remove_from_queue(queuename, interface)) {
05482    case RES_OKAY:
05483       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05484       astman_send_ack(s, m, "Removed interface from queue");
05485       break;
05486    case RES_EXISTS:
05487       astman_send_error(s, m, "Unable to remove interface: Not there");
05488       break;
05489    case RES_NOSUCHQUEUE:
05490       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05491       break;
05492    case RES_OUTOFMEMORY:
05493       astman_send_error(s, m, "Out of memory");
05494       break;
05495    case RES_NOT_DYNAMIC:
05496       astman_send_error(s, m, "Member not dynamic");
05497       break;
05498    }
05499 
05500    return 0;
05501 }

static int member_cmp_fn ( void *  obj1,
void *  obj2,
int  flags 
) [static]

Definition at line 874 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00875 {
00876    struct member *mem1 = obj1, *mem2 = obj2;
00877    return strcasecmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00878 }

static int member_hash_fn ( const void *  obj,
const int  flags 
) [static]

Definition at line 862 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00863 {
00864    const struct member *mem = obj;
00865    const char *chname = strchr(mem->interface, '/');
00866    int ret = 0, i;
00867    if (!chname)
00868       chname = mem->interface;
00869    for (i = 0; i < 5 && chname[i]; i++)
00870       ret += compress_char(chname[i]) << (i * 6);
00871    return ret;
00872 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 493 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00494 {
00495    static unsigned int warned = 0;
00496    if (!warned) {
00497       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00498       warned = 1;
00499    }
00500 }

static int num_available_members ( struct call_queue q  )  [static]

Get the number of members available to accept a call.

Note:
The queue passed in should be locked prior to this function call
Parameters:
[in] q The queue for which we are couting the number of available members
Returns:
Return the number of available members in queue q

Definition at line 1836 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_ONHOLD, AST_DEVICE_RINGING, AST_DEVICE_RINGINUSE, AST_DEVICE_UNKNOWN, call_queue::autofill, call_queue::members, member::paused, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.

Referenced by compare_weight(), and is_our_turn().

01837 {
01838    struct member *mem;
01839    int avl = 0;
01840    struct ao2_iterator mem_iter;
01841 
01842    mem_iter = ao2_iterator_init(q->members, 0);
01843    while ((mem = ao2_iterator_next(&mem_iter))) {
01844       switch (mem->status) {
01845       case AST_DEVICE_INUSE:
01846          if (!q->ringinuse)
01847             break;
01848          /* else fall through */
01849       case AST_DEVICE_NOT_INUSE:
01850       case AST_DEVICE_ONHOLD:
01851       case AST_DEVICE_RINGINUSE:
01852       case AST_DEVICE_RINGING:
01853       case AST_DEVICE_UNKNOWN:
01854          if (!mem->paused) {
01855             avl++;
01856          }
01857          break;
01858       }
01859       ao2_ref(mem, -1);
01860 
01861       /* If autofill is not enabled or if the queue's strategy is ringall, then
01862        * we really don't care about the number of available members so much as we
01863        * do that there is at least one available.
01864        *
01865        * In fact, we purposely will return from this function stating that only
01866        * one member is available if either of those conditions hold. That way,
01867        * functions which determine what action to take based on the number of available
01868        * members will operate properly. The reasoning is that even if multiple
01869        * members are available, only the head caller can actually be serviced.
01870        */
01871       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
01872          break;
01873       }
01874    }
01875    ao2_iterator_destroy(&mem_iter);
01876 
01877    return avl;
01878 }

static int play_file ( struct ast_channel chan,
char *  filename 
) [static]

Definition at line 1594 of file app_queue.c.

References AST_DIGIT_ANY, ast_fileexists(), ast_stopstream(), ast_streamfile(), ast_strlen_zero(), and ast_waitstream().

Referenced by say_periodic_announcement(), say_position(), and try_calling().

01595 {
01596    int res;
01597 
01598    if (ast_strlen_zero(filename)) {
01599       return 0;
01600    }
01601 
01602    if (!ast_fileexists(filename, NULL, chan->language)) {
01603       return 0;
01604    }
01605 
01606    ast_stopstream(chan);
01607 
01608    res = ast_streamfile(chan, filename, chan->language);
01609    if (!res)
01610       res = ast_waitstream(chan, AST_DIGIT_ANY);
01611 
01612    ast_stopstream(chan);
01613 
01614    return res;
01615 }

static int pqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3928 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().

03929 {
03930    struct ast_module_user *lu;
03931    char *parse;
03932    int priority_jump = 0;
03933    int ignore_fail = 0;
03934    AST_DECLARE_APP_ARGS(args,
03935       AST_APP_ARG(queuename);
03936       AST_APP_ARG(interface);
03937       AST_APP_ARG(options);
03938    );
03939 
03940    if (ast_strlen_zero(data)) {
03941       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03942       return -1;
03943    }
03944 
03945    parse = ast_strdupa(data);
03946 
03947    AST_STANDARD_APP_ARGS(args, parse);
03948 
03949    lu = ast_module_user_add(chan);
03950 
03951    if (args.options) {
03952       if (strchr(args.options, 'j'))
03953          priority_jump = 1;
03954       if (strchr(args.options, 'i'))
03955          ignore_fail = 1;
03956    }
03957 
03958    if (ast_strlen_zero(args.interface)) {
03959       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03960       ast_module_user_remove(lu);
03961       return -1;
03962    }
03963 
03964    if (set_member_paused(args.queuename, args.interface, 1)) {
03965       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03966       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03967       if (priority_jump || ast_opt_priority_jumping) {
03968          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03969             ast_module_user_remove(lu);
03970             return 0;
03971          }
03972       }
03973       ast_module_user_remove(lu);
03974       if (ignore_fail) {
03975          return 0;
03976       } else {
03977          return -1;
03978       }
03979    }
03980 
03981    ast_module_user_remove(lu);
03982    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03983    return 0;
03984 }

static int ql_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 4186 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().

04187 {
04188    struct ast_module_user *u;
04189    char *parse;
04190 
04191    AST_DECLARE_APP_ARGS(args,
04192       AST_APP_ARG(queuename);
04193       AST_APP_ARG(uniqueid);
04194       AST_APP_ARG(membername);
04195       AST_APP_ARG(event);
04196       AST_APP_ARG(params);
04197    );
04198 
04199    if (ast_strlen_zero(data)) {
04200       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04201       return -1;
04202    }
04203 
04204    u = ast_module_user_add(chan);
04205 
04206    parse = ast_strdupa(data);
04207 
04208    AST_STANDARD_APP_ARGS(args, parse);
04209 
04210    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04211        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04212       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04213       ast_module_user_remove(u);
04214       return -1;
04215    }
04216 
04217    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04218       "%s", args.params ? args.params : "");
04219 
04220    ast_module_user_remove(u);
04221 
04222    return 0;
04223 }

static int qmc_handler ( const char *  queuename,
char *  buffer,
int  len 
) [static]

Definition at line 5748 of file app_queue.c.

References member_count::active, member_count::all, member_count::free, member_count::inuse, member_count::paused, queue_member_count(), RESULT_FAILURE, RESULT_SUCCESS, and member_count::valid.

Referenced by cli_queue_member_count(), and manager_queue_member_count().

05749 {
05750       struct member_count qmc;
05751       memset(&qmc, 0, sizeof(qmc));
05752       if (queue_member_count(queuename, &qmc) != 0) {
05753             return RESULT_FAILURE; 
05754       } else {
05755             snprintf(buffer, len, 
05756                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05757                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05758             return RESULT_SUCCESS;
05759       }
05760 }

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 unless some condition (such as an expiration time) causes us to exit the queue.

Definition at line 4237 of file app_queue.c.

References call_queue::announcefrequency, ao2_ref(), AST_APP_ARG, AST_CONTROL_RINGING, AST_DECLARE_APP_ARGS, ast_indicate(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_moh_start(), ast_moh_stop(), ast_queue_log(), AST_STANDARD_APP_ARGS, ast_stopstream(), ast_strdupa, ast_strlen_zero(), ast_verbose(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::moh, queue_ent::opos, option_debug, option_verbose, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, QUEUE_CONTINUE, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, stop, try_calling(), update_realtime_members(), queue_ent::valid_digits, VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04238 {
04239    int res=-1;
04240    int ringing=0;
04241    struct ast_module_user *lu;
04242    const char *user_priority;
04243    const char *max_penalty_str;
04244    int prio;
04245    int qcontinue = 0;
04246    int max_penalty;
04247    enum queue_result reason = QUEUE_UNKNOWN;
04248    /* whether to exit Queue application after the timeout hits */
04249    int tries = 0;
04250    int noption = 0;
04251    char *parse;
04252    AST_DECLARE_APP_ARGS(args,
04253       AST_APP_ARG(queuename);
04254       AST_APP_ARG(options);
04255       AST_APP_ARG(url);
04256       AST_APP_ARG(announceoverride);
04257       AST_APP_ARG(queuetimeoutstr);
04258       AST_APP_ARG(agi);
04259    );
04260    /* Our queue entry */
04261    struct queue_ent qe = { 0 };
04262    
04263    if (ast_strlen_zero(data)) {
04264       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04265       return -1;
04266    }
04267    
04268    parse = ast_strdupa(data);
04269    AST_STANDARD_APP_ARGS(args, parse);
04270 
04271    lu = ast_module_user_add(chan);
04272 
04273    /* Setup our queue entry */
04274    qe.start = time(NULL);
04275 
04276    /* set the expire time based on the supplied timeout; */
04277    if (!ast_strlen_zero(args.queuetimeoutstr))
04278       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04279    else
04280       qe.expire = 0;
04281 
04282    /* Get the priority from the variable ${QUEUE_PRIO} */
04283    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04284    if (user_priority) {
04285       if (sscanf(user_priority, "%30d", &prio) == 1) {
04286          if (option_debug)
04287             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04288                chan->name, prio);
04289       } else {
04290          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04291             user_priority, chan->name);
04292          prio = 0;
04293       }
04294    } else {
04295       if (option_debug > 2)
04296          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04297       prio = 0;
04298    }
04299 
04300    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04301    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04302       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04303          if (option_debug)
04304             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04305                chan->name, max_penalty);
04306       } else {
04307          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04308             max_penalty_str, chan->name);
04309          max_penalty = 0;
04310       }
04311    } else {
04312       max_penalty = 0;
04313    }
04314 
04315    if (args.options && (strchr(args.options, 'r')))
04316       ringing = 1;
04317 
04318    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
04319       qe.ring_when_ringing = 1;
04320    }
04321 
04322    if (args.options && (strchr(args.options, 'c')))
04323       qcontinue = 1;
04324 
04325    if (option_debug)
04326       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04327          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04328 
04329    qe.chan = chan;
04330    qe.prio = prio;
04331    qe.max_penalty = max_penalty;
04332    qe.last_pos_said = 0;
04333    qe.last_pos = 0;
04334    qe.last_periodic_announce_time = time(NULL);
04335    qe.last_periodic_announce_sound = 0;
04336    qe.valid_digits = 0;
04337    if (!join_queue(args.queuename, &qe, &reason)) {
04338       int makeannouncement = 0;
04339 
04340       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04341          S_OR(chan->cid.cid_num, ""));
04342 check_turns:
04343       if (ringing) {
04344          ast_indicate(chan, AST_CONTROL_RINGING);
04345       } else {
04346          ast_moh_start(chan, qe.moh, NULL);
04347       }
04348 
04349       /* This is the wait loop for callers 2 through maxlen */
04350       res = wait_our_turn(&qe, ringing, &reason);
04351       if (res)
04352          goto stop;
04353 
04354       for (;;) {
04355          /* This is the wait loop for the head caller*/
04356          /* To exit, they may get their call answered; */
04357          /* they may dial a digit from the queue context; */
04358          /* or, they may timeout. */
04359 
04360          enum queue_member_status stat;
04361 
04362          /* Leave if we have exceeded our queuetimeout */
04363          if (qe.expire && (time(NULL) >= qe.expire)) {
04364             record_abandoned(&qe);
04365             reason = QUEUE_TIMEOUT;
04366             res = 0;
04367             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04368             break;
04369          }
04370 
04371          if (makeannouncement) {
04372             /* Make a position announcement, if enabled */
04373             if (qe.parent->announcefrequency && !ringing)
04374                if ((res = say_position(&qe)))
04375                   goto stop;
04376 
04377          }
04378          makeannouncement = 1;
04379 
04380          /* Leave if we have exceeded our queuetimeout */
04381          if (qe.expire && (time(NULL) >= qe.expire)) {
04382             record_abandoned(&qe);
04383             reason = QUEUE_TIMEOUT;
04384             res = 0;
04385             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04386             break;
04387          }
04388          /* Make a periodic announcement, if enabled */
04389          if (qe.parent->periodicannouncefrequency && !ringing)
04390             if ((res = say_periodic_announcement(&qe)))
04391                goto stop;
04392 
04393          /* Leave if we have exceeded our queuetimeout */
04394          if (qe.expire && (time(NULL) >= qe.expire)) {
04395             record_abandoned(&qe);
04396             reason = QUEUE_TIMEOUT;
04397             res = 0;
04398             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04399             break;
04400          }
04401          /* Try calling all queue members for 'timeout' seconds */
04402          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04403          if (res)
04404             goto stop;
04405 
04406          stat = get_member_status(qe.parent, qe.max_penalty);
04407 
04408          /* exit after 'timeout' cycle if 'n' option enabled */
04409          if (noption && tries >= qe.parent->membercount) {
04410             if (option_verbose > 2)
04411                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04412             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04413             record_abandoned(&qe);
04414             reason = QUEUE_TIMEOUT;
04415             res = 0;
04416             break;
04417          }
04418 
04419          /* leave the queue if no agents, if enabled */
04420          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04421             record_abandoned(&qe);
04422             reason = QUEUE_LEAVEEMPTY;
04423             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04424             res = 0;
04425             break;
04426          }
04427 
04428          /* leave the queue if no reachable agents, if enabled */
04429          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04430             record_abandoned(&qe);
04431             reason = QUEUE_LEAVEUNAVAIL;
04432             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04433             res = 0;
04434             break;
04435          }
04436 
04437          /* Leave if we have exceeded our queuetimeout */
04438          if (qe.expire && (time(NULL) >= qe.expire)) {
04439             record_abandoned(&qe);
04440             reason = QUEUE_TIMEOUT;
04441             res = 0;
04442             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04443             break;
04444          }
04445 
04446          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04447          update_realtime_members(qe.parent);
04448 
04449          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04450          res = wait_a_bit(&qe);
04451          if (res)
04452             goto stop;
04453 
04454          /* Since this is a priority queue and
04455           * it is not sure that we are still at the head
04456           * of the queue, go and check for our turn again.
04457           */
04458          if (!is_our_turn(&qe)) {
04459             if (option_debug)
04460                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04461                   qe.chan->name);
04462             goto check_turns;
04463          }
04464       }
04465 
04466 stop:
04467       if (res) {
04468          if (res < 0) {
04469             res = -1;
04470             if (!qe.handled) {
04471                record_abandoned(&qe);
04472                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04473                   "%d|%d|%ld", qe.pos, qe.opos,
04474                   (long) time(NULL) - qe.start);
04475             } else if (qcontinue) {
04476                reason = QUEUE_CONTINUE;
04477                res = 0;
04478             }
04479          } else if (qe.valid_digits) {
04480             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04481                "%s|%d", qe.digits, qe.pos);
04482          }
04483       }
04484 
04485       /* Don't allow return code > 0 */
04486       if (res >= 0) {
04487          res = 0; 
04488          if (ringing) {
04489             ast_indicate(chan, -1);
04490          } else {
04491             ast_moh_stop(chan);
04492          }        
04493          ast_stopstream(chan);
04494       }
04495       leave_queue(&qe);
04496       if (reason != QUEUE_UNKNOWN)
04497          set_queue_result(chan, reason);
04498    } else {
04499       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04500       set_queue_result(chan, reason);
04501       res = 0;
04502    }
04503    if (qe.parent) {
04504       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
04505        * This ref must be taken away right before the queue_ent is destroyed.  In this case
04506        * the queue_ent is about to be returned on the stack */
04507       ao2_ref(qe.parent, -1);
04508    }
04509    ast_module_user_remove(lu);
04510 
04511    return res;
04512 }

static int queue_function_exists ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 4820 of file app_queue.c.

References ast_log(), ast_module_user_add, ast_module_user_remove, ast_strlen_zero(), load_realtime_queue(), and LOG_ERROR.

04821 {
04822   struct ast_module_user *lu;
04823   buf[0] = '\0';
04824   
04825   if (ast_strlen_zero(data)) {
04826     ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04827     return -1;
04828   }
04829   lu = ast_module_user_add(chan);
04830   snprintf(buf, len, "%d", load_realtime_queue(data) != NULL? 1 : 0);
04831   ast_module_user_remove(lu);
04832   
04833   return 0;
04834 }

static int queue_function_queuemembercount ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 4522 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_log(), ast_module_user_add, ast_module_user_remove, ast_strdupa, ast_strlen_zero(), load_realtime_queue(), LOG_ERROR, LOG_WARNING, call_queue::members, name, member::paused, QMC_ACTIVE, QMC_ALL, QMC_FREE, QMC_PAUSED, QMC_VALID, and member::status.

04523 {
04524    int count = 0;
04525    struct call_queue *q;
04526    struct ast_module_user *lu;
04527    struct member *m;
04528    struct ao2_iterator mem_iter;
04529    char *name, *item;
04530    enum qmc_status mode = QMC_VALID;
04531 
04532    buf[0] = '\0';
04533    
04534    if (ast_strlen_zero(data)) {
04535       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04536       return -1;
04537    }
04538 
04539    name = ast_strdupa(data);
04540 
04541    lu = ast_module_user_add(chan);
04542 
04543    if ((item = strchr(name, ':'))) {
04544       *item = '\0';
04545       item++;
04546    } else {
04547       item = "";
04548    }
04549 
04550    if (!strcasecmp(item, "valid")) {
04551       mode = QMC_VALID;
04552    } else  if (!strcasecmp(item, "paused")) {
04553       mode = QMC_PAUSED;
04554    } else  if (!strcasecmp(item, "active")) {
04555       mode = QMC_ACTIVE;
04556    } else  if (!strcasecmp(item, "free")) {
04557       mode = QMC_FREE;
04558    } else  if (!strcasecmp(item, "all")) {
04559       mode = QMC_ALL;
04560    }
04561 
04562    if ((q = load_realtime_queue(name))) {
04563       ao2_lock(q);
04564       mem_iter = ao2_iterator_init(q->members, 0);
04565       while ((m = ao2_iterator_next(&mem_iter))) {
04566          switch (mode) {
04567          case QMC_VALID:
04568             /* Count the queue members who are logged in and presently answering calls */
04569             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04570                count++;
04571             }
04572             break;
04573          case QMC_PAUSED:
04574             /* Count paused members */
04575             if (m->paused) {
04576                count++;
04577             }
04578             break;
04579          case QMC_ACTIVE:
04580             /* Count not paused members who are logged in and presently answering calls */
04581             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04582                count++;
04583             }
04584             break;
04585          case QMC_FREE:
04586             /* Count free members in the queue */
04587             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04588                count++;
04589             }
04590             break;
04591          default:
04592             count++;
04593             break;
04594          }
04595          ao2_ref(m, -1);
04596       }
04597       ao2_iterator_destroy(&mem_iter);
04598       ao2_unlock(q);
04599    } else
04600       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04601 
04602    snprintf(buf, len, "%d", count);
04603    ast_module_user_remove(lu);
04604 
04605    return 0;
04606 }

static int queue_function_queuememberlist ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 4651 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.

04652 {
04653    struct ast_module_user *u;
04654    struct call_queue *q;
04655    struct member *m;
04656 
04657    /* Ensure an otherwise empty list doesn't return garbage */
04658    buf[0] = '\0';
04659 
04660    if (ast_strlen_zero(data)) {
04661       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04662       return -1;
04663    }
04664    
04665    u = ast_module_user_add(chan);
04666 
04667    AST_LIST_LOCK(&queues);
04668    AST_LIST_TRAVERSE(&queues, q, list) {
04669       if (!strcasecmp(q->name, data)) {
04670          ao2_lock(q);
04671          break;
04672       }
04673    }
04674    AST_LIST_UNLOCK(&queues);
04675 
04676    if (q) {
04677       int buflen = 0, count = 0;
04678       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04679 
04680       while ((m = ao2_iterator_next(&mem_iter))) {
04681          /* strcat() is always faster than printf() */
04682          if (count++) {
04683             strncat(buf + buflen, ",", len - buflen - 1);
04684             buflen++;
04685          }
04686          strncat(buf + buflen, m->interface, len - buflen - 1);
04687          buflen += strlen(m->interface);
04688          /* Safeguard against overflow (negative length) */
04689          if (buflen >= len - 2) {
04690             ao2_ref(m, -1);
04691             ast_log(LOG_WARNING, "Truncating list\n");
04692             break;
04693          }
04694          ao2_ref(m, -1);
04695       }
04696       ao2_iterator_destroy(&mem_iter);
04697       ao2_unlock(q);
04698    } else
04699       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04700 
04701    /* We should already be terminated, but let's make sure. */
04702    buf[len - 1] = '\0';
04703    ast_module_user_remove(u);
04704 
04705    return 0;
04706 }

static int queue_function_queuememberpaused ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 4764 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), member_interface::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, and member::paused.

04765 {
04766    struct ast_module_user *lu;
04767    struct call_queue *q;
04768    struct member *cur;
04769    struct ao2_iterator mem_iter;
04770    char tmp[128] = "";
04771    char *buffer;
04772 
04773    AST_DECLARE_APP_ARGS(args,
04774       AST_APP_ARG(queue);
04775       AST_APP_ARG(interface);
04776    );
04777 
04778    AST_STANDARD_APP_ARGS(args, data);
04779    if (ast_strlen_zero(args.interface)) {
04780       ast_log(LOG_WARNING, "This function requires an interface name.\n");
04781       return -1;
04782    } 
04783    lu = ast_module_user_add(chan);
04784 
04785    buffer = ast_malloc(len);
04786    buffer[0]='\0';
04787 
04788    AST_LIST_LOCK(&queues);
04789    AST_LIST_TRAVERSE(&queues, q, list) {
04790       ao2_lock(q);
04791       if (ast_strlen_zero(args.queue) ||
04792             (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue)))
04793             ) {
04794             /* Iterate over queue members */
04795             mem_iter = ao2_iterator_init(q->members, 0);
04796             while ((cur = ao2_iterator_next(&mem_iter))) {
04797                   if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) {
04798                         if (!ast_strlen_zero(args.queue)) {
04799                               ast_copy_string(buffer, cur->paused?"1":"0", len);
04800                         } else {
04801                               snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, cur->paused?"1":"0");
04802                               strncat(buffer, tmp, sizeof(tmp));
04803                         }
04804                         ao2_ref(cur, -1);
04805                         break;
04806                   }
04807                   ao2_ref(cur, -1);
04808             }
04809       }
04810       ao2_unlock(q);
04811    }
04812    AST_LIST_UNLOCK(&queues);
04813    ast_copy_string(buf, buffer, len);
04814    ast_free(buffer);
04815 
04816    ast_module_user_remove(lu);
04817    return 0;
04818 }

static int queue_function_queuememberstatus ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 4708 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_APP_ARG, ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_malloc, ast_module_user_add, ast_module_user_remove, AST_STANDARD_APP_ARGS, ast_strlen_zero(), devstate2str(), member_interface::interface, LOG_WARNING, member::membername, call_queue::members, call_queue::name, and member::status.

04709 {
04710    struct ast_module_user *lu;
04711    struct call_queue *q;
04712    struct member *cur;
04713    struct ao2_iterator mem_iter;
04714    char tmp[128] = "";
04715    char *buffer;
04716 
04717    AST_DECLARE_APP_ARGS(args,
04718       AST_APP_ARG(queue);
04719       AST_APP_ARG(interface);
04720    );
04721 
04722    AST_STANDARD_APP_ARGS(args, data);
04723    if (ast_strlen_zero(args.interface)) {
04724       ast_log(LOG_WARNING, "This function requires an interface name.\n");
04725       return -1;
04726    } 
04727    lu = ast_module_user_add(chan);
04728 
04729    buffer = ast_malloc(len);
04730    buffer[0]='\0';
04731 
04732    AST_LIST_LOCK(&queues);
04733    AST_LIST_TRAVERSE(&queues, q, list) {
04734       ao2_lock(q);
04735       if (ast_strlen_zero(args.queue) ||
04736             (!ast_strlen_zero(args.queue) && !strncmp(args.queue, q->name, strlen(args.queue)))
04737             ) {
04738             /* Iterate over queue members */
04739             mem_iter = ao2_iterator_init(q->members, 0);
04740             while ((cur = ao2_iterator_next(&mem_iter))) {
04741                   if (!strncasecmp(args.interface, cur->membername, strlen(args.interface))) {
04742                         if (!ast_strlen_zero(args.queue)) {
04743                               ast_copy_string(buffer, devstate2str(cur->status), len);
04744                         } else {
04745                                         snprintf(tmp, sizeof(tmp), "%s%s:%s", ast_strlen_zero(tmp)?"":",", q->name, devstate2str(cur->status));
04746                               strncat(buffer, tmp, sizeof(tmp));
04747                         }
04748                         ao2_ref(cur, -1);
04749                         continue;
04750                   }
04751                   ao2_ref(cur, -1);
04752             }
04753       }
04754       ao2_unlock(q);
04755    }
04756    AST_LIST_UNLOCK(&queues);
04757    ast_copy_string(buf, buffer, len);
04758    ast_free(buffer);
04759 
04760    ast_module_user_remove(lu);
04761    return 0;
04762 }

static int queue_function_queuewaitingcount ( struct ast_channel chan,
char *  cmd,
char *  data,
char *  buf,
size_t  len 
) [static]

Definition at line 4608 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.

04609 {
04610    int count = 0;
04611    struct call_queue *q;
04612    struct ast_module_user *lu;
04613    struct ast_variable *var = NULL;
04614 
04615    buf[0] = '\0';
04616    
04617    if (ast_strlen_zero(data)) {
04618       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04619       return -1;
04620    }
04621 
04622    lu = ast_module_user_add(chan);
04623    
04624    AST_LIST_LOCK(&queues);
04625    AST_LIST_TRAVERSE(&queues, q, list) {
04626       if (!strcasecmp(q->name, data)) {
04627          ao2_lock(q);
04628          break;
04629       }
04630    }
04631    AST_LIST_UNLOCK(&queues);
04632 
04633    if (q) {
04634       count = q->count;
04635       ao2_unlock(q);
04636    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04637       /* if the queue is realtime but was not found in memory, this
04638        * means that the queue had been deleted from memory since it was 
04639        * "dead." This means it has a 0 waiting count
04640        */
04641       count = 0;
04642       ast_variables_destroy(var);
04643    } else
04644       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04645 
04646    snprintf(buf, len, "%d", count);
04647    ast_module_user_remove(lu);
04648    return 0;
04649 }

static int queue_member_count ( const char *  qname,
struct member_count qmc 
) [static]

Definition at line 5706 of file app_queue.c.

References member_count::active, member_count::all, ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INUSE, AST_DEVICE_INVALID, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNAVAILABLE, AST_DEVICE_UNKNOWN, ast_log(), member_count::free, member_count::inuse, load_realtime_queue(), LOG_WARNING, call_queue::members, member_count::paused, member::paused, member::status, and member_count::valid.

Referenced by qmc_handler().

05707 {
05708       int res = 0;
05709       struct call_queue *q;
05710       struct member *m;
05711       struct ao2_iterator mem_iter;
05712       
05713       if ((q = load_realtime_queue(qname))) {
05714             ao2_lock(q);
05715             mem_iter = ao2_iterator_init(q->members, 0);
05716             while ((m = ao2_iterator_next(&mem_iter))) {
05717                   /* Count the queue members in use */
05718                   if (m->status == AST_DEVICE_INUSE) {
05719                         qmc->inuse++;
05720                   }
05721                   /* Count the queue members who are logged in and presently answering calls */
05722                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05723                         qmc->valid++;
05724                   }
05725                   /* Count paused members */
05726                   if (m->paused) {
05727                         qmc->paused++;
05728                   }
05729                   /* Count not paused members who are logged in and presently answering calls */
05730                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05731                         qmc->active++;
05732                   }
05733                   /* Count free members in the queue */
05734                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05735                         qmc->free++;
05736                   }
05737                   qmc->all++;
05738                   ao2_ref(m, -1);
05739             }
05740             ao2_unlock(q);
05741       } else {
05742             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05743             res = -1;
05744       }
05745       return res;
05746 }

static void queue_set_param ( struct call_queue q,
const char *  param,
const char *  val,
int  linenum,
int  failunknown 
) [static]

Configure a queue parameter.

For error reporting, line number is passed for .conf static configuration. For Realtime queues, linenum is -1. The failunknown flag is set for config files (and static realtime) to show errors for unknown parameters. It is cleared for dynamic realtime to allow extra fields in the tables.

Definition at line 1041 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, ast_copy_string(), ast_log(), ast_strdupa, ast_true(), call_queue::autofill, call_queue::autopause, call_queue::context, DEFAULT_RETRY, DEFAULT_TIMEOUT, call_queue::eventwhencalled, call_queue::joinempty, call_queue::leavewhenempty, LOG_WARNING, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, call_queue::memberdelay, call_queue::moh, call_queue::monfmt, call_queue::monjoin, monjoin_dep_warning(), call_queue::montype, call_queue::name, call_queue::periodicannouncefrequency, QUEUE_EMPTY_NORMAL, QUEUE_EMPTY_STRICT, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_RINGALL, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, s, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, strat2int(), call_queue::strategy, strsep(), call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

01042 {
01043    if (!strcasecmp(param, "musicclass") || 
01044       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01045       ast_copy_string(q->moh, val, sizeof(q->moh));
01046    } else if (!strcasecmp(param, "announce")) {
01047       ast_copy_string(q->announce, val, sizeof(q->announce));
01048    } else if (!strcasecmp(param, "context")) {
01049       ast_copy_string(q->context, val, sizeof(q->context));
01050    } else if (!strcasecmp(param, "timeout")) {
01051       q->timeout = atoi(val);
01052       if (q->timeout < 0)
01053          q->timeout = DEFAULT_TIMEOUT;
01054    } else if (!strcasecmp(param, "ringinuse")) {
01055       q->ringinuse = ast_true(val);
01056    } else if (!strcasecmp(param, "setinterfacevar")) {
01057       q->setinterfacevar = ast_true(val);
01058    } else if (!strcasecmp(param, "monitor-join")) {
01059       monjoin_dep_warning();
01060       q->monjoin = ast_true(val);
01061    } else if (!strcasecmp(param, "monitor-format")) {
01062       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01063    } else if (!strcasecmp(param, "queue-youarenext")) {
01064       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
01065    } else if (!strcasecmp(param, "queue-thereare")) {
01066       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
01067    } else if (!strcasecmp(param, "queue-callswaiting")) {
01068       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
01069    } else if (!strcasecmp(param, "queue-holdtime")) {
01070       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
01071    } else if (!strcasecmp(param, "queue-minutes")) {
01072       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
01073    } else if (!strcasecmp(param, "queue-minute")) {
01074       ast_copy_string(q->sound_minute, val, sizeof(q->sound_minute));
01075    } else if (!strcasecmp(param, "queue-seconds")) {
01076       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
01077    } else if (!strcasecmp(param, "queue-thankyou")) {
01078       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
01079    } else if (!strcasecmp(param, "queue-reporthold")) {
01080       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
01081    } else if (!strcasecmp(param, "announce-frequency")) {
01082       q->announcefrequency = atoi(val);
01083    } else if (!strcasecmp(param, "announce-round-seconds")) {
01084       q->roundingseconds = atoi(val);
01085       /* Rounding to any other values just doesn't make sense... */
01086       if (!(q->roundingseconds == 0 || q->roundingseconds == 5 || q->roundingseconds == 10
01087             || q->roundingseconds == 15 || q->roundingseconds == 20 || q->roundingseconds == 30)) {
01088          if (linenum >= 0) {
01089             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01090                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01091                val, param, q->name, linenum);
01092          } else {
01093             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01094                "using 0 instead for queue '%s'\n", val, param, q->name);
01095          }
01096          q->roundingseconds=0;
01097       }
01098    } else if (!strcasecmp(param, "announce-holdtime")) {
01099       if (!strcasecmp(val, "once"))
01100          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01101       else if (ast_true(val))
01102          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01103       else
01104          q->announceholdtime = 0;
01105    } else if (!strcasecmp(param, "periodic-announce")) {
01106       if (strchr(val, '|')) {
01107          char *s, *buf = ast_strdupa(val);
01108          unsigned int i = 0;
01109 
01110          while ((s = strsep(&buf, "|"))) {
01111             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
01112             i++;
01113             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01114                break;
01115          }
01116       } else {
01117          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
01118       }
01119    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01120       q->periodicannouncefrequency = atoi(val);
01121    } else if (!strcasecmp(param, "retry")) {
01122       q->retry = atoi(val);
01123       if (q->retry <= 0)
01124          q->retry = DEFAULT_RETRY;
01125    } else if (!strcasecmp(param, "wrapuptime")) {
01126       q->wrapuptime = atoi(val);
01127    } else if (!strcasecmp(param, "autofill")) {
01128       q->autofill = ast_true(val);
01129    } else if (!strcasecmp(param, "monitor-type")) {
01130       if (!strcasecmp(val, "mixmonitor"))
01131          q->montype = 1;
01132    } else if (!strcasecmp(param, "autopause")) {
01133       q->autopause = ast_true(val);
01134    } else if (!strcasecmp(param, "maxlen")) {
01135       q->maxlen = atoi(val);
01136       if (q->maxlen < 0)
01137          q->maxlen = 0;
01138    } else if (!strcasecmp(param, "ringlimit")) {
01139       q->ringlimit = atoi(val);
01140       if (q->ringlimit < 0)
01141          q->ringlimit = 0;
01142    } else if (!strcasecmp(param, "servicelevel")) {
01143       q->servicelevel= atoi(val);
01144    } else if (!strcasecmp(param, "strategy")) {
01145       q->strategy = strat2int(val);
01146       if (q->strategy < 0) {
01147          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01148             val, q->name);
01149          q->strategy = QUEUE_STRATEGY_RINGALL;
01150       }
01151    } else if (!strcasecmp(param, "joinempty")) {
01152       if (!strcasecmp(val, "strict"))
01153          q->joinempty = QUEUE_EMPTY_STRICT;
01154       else if (ast_true(val))
01155          q->joinempty = QUEUE_EMPTY_NORMAL;
01156       else
01157          q->joinempty = 0;
01158    } else if (!strcasecmp(param, "leavewhenempty")) {
01159       if (!strcasecmp(val, "strict"))
01160          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01161       else if (ast_true(val))
01162          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01163       else
01164          q->leavewhenempty = 0;
01165    } else if (!strcasecmp(param, "eventmemberstatus")) {
01166       q->maskmemberstatus = !ast_true(val);
01167    } else if (!strcasecmp(param, "eventwhencalled")) {
01168       if (!strcasecmp(val, "vars")) {
01169          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01170       } else {
01171          q->eventwhencalled = ast_true(val) ? 1 : 0;
01172       }
01173    } else if (!strcasecmp(param, "reportholdtime")) {
01174       q->reportholdtime = ast_true(val);
01175    } else if (!strcasecmp(param, "memberdelay")) {
01176       q->memberdelay = atoi(val);
01177    } else if (!strcasecmp(param, "weight")) {
01178       q->weight = atoi(val);
01179       if (q->weight)
01180          use_weight++;
01181       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01182          we will not see any effect on use_weight until next reload. */
01183    } else if (!strcasecmp(param, "timeoutrestart")) {
01184       q->timeoutrestart = ast_true(val);
01185    } else if (failunknown) {
01186       if (linenum >= 0) {
01187          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01188             q->name, param, linenum);
01189       } else {
01190          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01191       }
01192    }
01193 }

static int queue_show ( int  fd,
int  argc,
char **  argv 
) [static]

Definition at line 5278 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

05279 {
05280    return __queues_show(NULL, 0, fd, argc, argv);
05281 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2866 of file app_queue.c.

References ast_free.

02867 {
02868    struct queue_transfer_ds *qtds = data;
02869    ast_free(qtds);
02870 }

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 2889 of file app_queue.c.

References ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_log(), ast_queue_log(), queue_transfer_ds::callcompletedinsl, queue_ent::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, queue_transfer_ds::member, member::membername, call_queue::name, queue_ent::parent, queue_transfer_ds::qe, queue_transfer_info, queue_ent::start, queue_transfer_ds::starttime, and update_queue().

02890 {
02891    struct queue_transfer_ds *qtds = data;
02892    struct queue_ent *qe = qtds->qe;
02893    struct member *member = qtds->member;
02894    time_t callstart = qtds->starttime;
02895    int callcompletedinsl = qtds->callcompletedinsl;
02896    struct ast_datastore *datastore;
02897 
02898    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02899             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02900             (long) (time(NULL) - callstart));
02901 
02902    update_queue(qe->parent, member, callcompletedinsl);
02903    
02904    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02905    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02906       ast_channel_datastore_remove(old_chan, datastore);
02907    } else {
02908       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02909    }
02910 }

static void recalc_holdtime ( struct queue_ent qe,
int  newholdtime 
) [static]

Definition at line 1756 of file app_queue.c.

References ao2_lock(), ao2_unlock(), call_queue::holdtime, and queue_ent::parent.

Referenced by try_calling().

01757 {
01758    int oldvalue;
01759 
01760    /* Calculate holdtime using an exponential average */
01761    /* Thanks to SRT for this contribution */
01762    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01763 
01764    ao2_lock(qe->parent);
01765    oldvalue = qe->parent->holdtime;
01766    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01767    ao2_unlock(qe->parent);
01768 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2278 of file app_queue.c.

References ao2_lock(), ao2_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, manager_event(), call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, and queue_ent::start.

Referenced by queue_exec(), and try_calling().

02279 {
02280    ao2_lock(qe->parent);
02281    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02282       "Queue: %s\r\n"
02283       "Uniqueid: %s\r\n"
02284       "Position: %d\r\n"
02285       "OriginalPosition: %d\r\n"
02286       "HoldTime: %d\r\n",
02287       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02288 
02289    qe->parent->callsabandoned++;
02290    ao2_unlock(qe->parent);
02291 }

static int reload ( void   )  [static]

Definition at line 5950 of file app_queue.c.

References reload_queues().

05951 {
05952    reload_queues();
05953    return 0;
05954 }

static void reload_queue_members ( void   )  [static]

Definition at line 3831 of file app_queue.c.

References add_to_queue(), ao2_lock(), ao2_unlock(), ast_db_del(), ast_db_freetree(), ast_db_get(), ast_db_gettree(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strlen_zero(), ERANGE, errno, member_interface::interface, ast_db_entry::key, load_realtime_queue(), LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, member::membername, call_queue::name, ast_db_entry::next, option_debug, member::paused, member::penalty, PM_MAX_LEN, RES_OUTOFMEMORY, member::state_interface, and strsep().

Referenced by load_module().

03832 {
03833    char *cur_ptr;
03834    char *queue_name;
03835    char *member;
03836    char *interface;
03837    char *membername = NULL;
03838    char *state_interface;
03839    char *penalty_tok;
03840    int penalty = 0;
03841    char *paused_tok;
03842    int paused = 0;
03843    struct ast_db_entry *db_tree;
03844    struct ast_db_entry *entry;
03845    struct call_queue *cur_queue;
03846    char queue_data[PM_MAX_LEN];
03847 
03848    AST_LIST_LOCK(&queues);
03849 
03850    /* Each key in 'pm_family' is the name of a queue */
03851    db_tree = ast_db_gettree(pm_family, NULL);
03852    for (entry = db_tree; entry; entry = entry->next) {
03853 
03854       queue_name = entry->key + strlen(pm_family) + 2;
03855 
03856       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03857          ao2_lock(cur_queue);
03858          if (!strcmp(queue_name, cur_queue->name))
03859             break;
03860          ao2_unlock(cur_queue);
03861       }
03862       
03863       if (!cur_queue)
03864          cur_queue = load_realtime_queue(queue_name);
03865 
03866       if (!cur_queue) {
03867          /* If the queue no longer exists, remove it from the
03868           * database */
03869          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03870          ast_db_del(pm_family, queue_name);
03871          continue;
03872       } else
03873          ao2_unlock(cur_queue);
03874 
03875       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03876          continue;
03877 
03878       cur_ptr = queue_data;
03879       while ((member = strsep(&cur_ptr, "|"))) {
03880          if (ast_strlen_zero(member))
03881             continue;
03882 
03883          interface = strsep(&member, ";");
03884          penalty_tok = strsep(&member, ";");
03885          paused_tok = strsep(&member, ";");
03886          membername = strsep(&member, ";");
03887          state_interface = strsep(&member,";");
03888 
03889          if (!penalty_tok) {
03890             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03891             break;
03892          }
03893          penalty = strtol(penalty_tok, NULL, 10);
03894          if (errno == ERANGE) {
03895             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03896             break;
03897          }
03898          
03899          if (!paused_tok) {
03900             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03901             break;
03902          }
03903          paused = strtol(paused_tok, NULL, 10);
03904          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03905             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03906             break;
03907          }
03908          if (ast_strlen_zero(membername))
03909             membername = interface;
03910 
03911          if (option_debug)
03912             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03913          
03914          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03915             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03916             break;
03917          }
03918       }
03919    }
03920 
03921    AST_LIST_UNLOCK(&queues);
03922    if (db_tree) {
03923       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03924       ast_db_freetree(db_tree);
03925    }
03926 }

static int reload_queues ( void   )  [static]

Definition at line 4905 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().

04906 {
04907    struct call_queue *q;
04908    struct ast_config *cfg;
04909    char *cat, *tmp;
04910    struct ast_variable *var;
04911    struct member *cur, *newm;
04912    struct ao2_iterator mem_iter;
04913    int new;
04914    const char *general_val = NULL;
04915    char *parse;
04916    char *interface, *state_interface;
04917    char *membername = NULL;
04918    int penalty;
04919    AST_DECLARE_APP_ARGS(args,
04920       AST_APP_ARG(interface);
04921       AST_APP_ARG(penalty);
04922       AST_APP_ARG(membername);
04923       AST_APP_ARG(state_interface);
04924    );
04925    
04926    if (!(cfg = ast_config_load("queues.conf"))) {
04927       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04928       return 0;
04929    }
04930    AST_LIST_LOCK(&queues);
04931    use_weight=0;
04932    /* Mark all non-realtime queues as dead for the moment */
04933    AST_LIST_TRAVERSE(&queues, q, list) {
04934       if (!q->realtime) {
04935          q->dead = 1;
04936          q->found = 0;
04937       }
04938    }
04939 
04940    /* Chug through config file */
04941    cat = NULL;
04942    while ((cat = ast_category_browse(cfg, cat)) ) {
04943       if (!strcasecmp(cat, "general")) {  
04944          /* Initialize global settings */
04945          queue_debug = 0;
04946          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04947             queue_debug = ast_true(general_val);
04948          queue_persistent_members = 0;
04949          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04950             queue_persistent_members = ast_true(general_val);
04951          autofill_default = 0;
04952          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04953             autofill_default = ast_true(general_val);
04954          montype_default = 0;
04955          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04956             if (!strcasecmp(general_val, "mixmonitor"))
04957                montype_default = 1;
04958          shared_lastcall = 0;
04959          if ((general_val = ast_variable_retrieve(cfg, "general", "shared_lastcall")))
04960             shared_lastcall = ast_true(general_val);
04961       } else { /* Define queue */
04962          /* Look for an existing one */
04963          AST_LIST_TRAVERSE(&queues, q, list) {
04964             if (!strcmp(q->name, cat))
04965                break;
04966          }
04967          if (!q) {
04968             /* Make one then */
04969             if (!(q = alloc_queue(cat))) {
04970                /* TODO: Handle memory allocation failure */
04971             }
04972             new = 1;
04973          } else
04974             new = 0;
04975          if (q) {
04976             const char *tmpvar;
04977             if (!new)
04978                ao2_lock(q);
04979             /* Check if a queue with this name already exists */
04980             if (q->found) {
04981                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04982                if (!new)
04983                   ao2_unlock(q);
04984                continue;
04985             }
04986 
04987             /* Due to the fact that the "rrordered" strategy will have a different allocation
04988              * scheme for queue members, we must devise the queue's strategy before other initializations.
04989              * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2
04990              * container used will have only a single bucket instead of the typical number.
04991              */
04992             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
04993                q->strategy = strat2int(tmpvar);
04994                if (q->strategy < 0) {
04995                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", tmpvar, q->name);
04996                   q->strategy = QUEUE_STRATEGY_RINGALL;
04997                }
04998             } else {
04999                q->strategy = QUEUE_STRATEGY_RINGALL;
05000             }
05001 
05002             /* Re-initialize the queue, and clear statistics */
05003             init_queue(q);
05004             clear_queue(q);
05005             mem_iter = ao2_iterator_init(q->members, 0);
05006             while ((cur = ao2_iterator_next(&mem_iter))) {
05007                if (!cur->dynamic) {
05008                   cur->delme = 1;
05009                }
05010                ao2_ref(cur, -1);
05011             }
05012             ao2_iterator_destroy(&mem_iter);
05013             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
05014                if (!strcasecmp(var->name, "member")) {
05015                   struct member tmpmem;
05016                   membername = NULL;
05017 
05018                   if (ast_strlen_zero(var->value)) {
05019                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
05020                      continue;
05021                   }
05022 
05023                   /* Add a new member */
05024                   if (!(parse = ast_strdup(var->value))) {
05025                      continue;
05026                   }
05027                   
05028                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
05029 
05030                   interface = args.interface;
05031                   if (!ast_strlen_zero(args.penalty)) {
05032                      tmp = ast_skip_blanks(args.penalty);
05033                      penalty = atoi(tmp);
05034                      if (penalty < 0) {
05035                         penalty = 0;
05036                      }
05037                   } else
05038                      penalty = 0;
05039 
05040                   if (!ast_strlen_zero(args.membername)) {
05041                      membername = ast_skip_blanks(args.membername);
05042                   }
05043 
05044                   if (!ast_strlen_zero(args.state_interface)) {
05045                      state_interface = ast_skip_blanks(args.state_interface);
05046                   } else {
05047                      state_interface = interface;
05048                   }
05049 
05050                   /* Find the old position in the list */
05051                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
05052                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
05053 
05054                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
05055                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
05056                      remove_from_interfaces(cur->state_interface);
05057                   }
05058 
05059                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
05060                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
05061                      add_to_interfaces(state_interface);
05062                   }
05063                   ao2_link(q->members, newm);
05064                   ao2_ref(newm, -1);
05065                   newm = NULL;
05066 
05067                   if (cur)
05068                      ao2_ref(cur, -1);
05069                   else {
05070                      q->membercount++;
05071                   }
05072                   ast_free(parse);
05073                } else {
05074                   queue_set_param(q, var->name, var->value, var->lineno, 1);
05075                }
05076             }
05077 
05078             /* Free remaining members marked as delme */
05079             mem_iter = ao2_iterator_init(q->members, 0);
05080             while ((cur = ao2_iterator_next(&mem_iter))) {
05081                if (! cur->delme) {
05082                   ao2_ref(cur, -1);
05083                   continue;
05084                }
05085 
05086                q->membercount--;
05087                ao2_unlink(q->members, cur);
05088                remove_from_interfaces(cur->state_interface);
05089                ao2_ref(cur, -1);
05090             }
05091             ao2_iterator_destroy(&mem_iter);
05092 
05093             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
05094                rr_dep_warning();
05095 
05096             if (new) {
05097                AST_LIST_INSERT_HEAD(&queues, q, list);
05098             } else
05099                ao2_unlock(q);
05100          }
05101       }
05102    }
05103    ast_config_destroy(cfg);
05104    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
05105       if (q->dead) {
05106          AST_LIST_REMOVE_CURRENT(&queues, list);
05107          ao2_ref(q, -1);
05108       } else {
05109          ao2_lock(q);
05110          mem_iter = ao2_iterator_init(q->members, 0);
05111          while ((cur = ao2_iterator_next(&mem_iter))) {
05112             if (cur->dynamic)
05113                q->membercount++;
05114             cur->status = ast_device_state(cur->state_interface);
05115             ao2_ref(cur, -1);
05116          }
05117          ao2_iterator_destroy(&mem_iter);
05118          ao2_unlock(q);
05119       }
05120    }
05121    AST_LIST_TRAVERSE_SAFE_END;
05122    AST_LIST_UNLOCK(&queues);
05123    return 1;
05124 }

static int remove_from_interfaces ( const char *  interface  )  [static]

Definition at line 1001 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().

01002 {
01003    struct member_interface *curint;
01004 
01005    if (interface_exists_global(interface))
01006       return 0;
01007 
01008    AST_LIST_LOCK(&interfaces);
01009    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
01010       if (!strcasecmp(curint->interface, interface)) {
01011          if (option_debug)
01012             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01013          AST_LIST_REMOVE_CURRENT(&interfaces, list);
01014          free(curint);
01015          break;
01016       }
01017    }
01018    AST_LIST_TRAVERSE_SAFE_END;
01019    AST_LIST_UNLOCK(&interfaces);
01020 
01021    return 0;
01022 }

static int remove_from_queue ( const char *  queuename,
const char *  interface 
) [static]

Definition at line 3679 of file app_queue.c.

References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, manager_event(), call_queue::membercount, member::membername, call_queue::members, call_queue::name, remove_from_interfaces(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and member::state_interface.

Referenced by handle_queue_remove_member(), manager_remove_queue_member(), and rqm_exec().

03680 {
03681    struct call_queue *q;
03682    struct member *mem, tmpmem;
03683    int res = RES_NOSUCHQUEUE;
03684 
03685    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03686 
03687    AST_LIST_LOCK(&queues);
03688    AST_LIST_TRAVERSE(&queues, q, list) {
03689       ao2_lock(q);
03690       if (strcmp(q->name, queuename)) {
03691          ao2_unlock(q);
03692          continue;
03693       }
03694 
03695       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03696          /* XXX future changes should beware of this assumption!! */
03697          if (!mem->dynamic) {
03698             res = RES_NOT_DYNAMIC;
03699             ao2_ref(mem, -1);
03700             ao2_unlock(q);
03701             break;
03702          }
03703          q->membercount--;
03704          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03705             "Queue: %s\r\n"
03706             "Location: %s\r\n"
03707             "MemberName: %s\r\n",
03708             q->name, mem->interface, mem->membername);
03709          ao2_unlink(q->members, mem);
03710          remove_from_interfaces(mem->state_interface);
03711          ao2_ref(mem, -1);
03712 
03713          if (queue_persistent_members)
03714             dump_queue_members(q);
03715          
03716          res = RES_OKAY;
03717       } else {
03718          res = RES_EXISTS;
03719       }
03720       ao2_unlock(q);
03721       break;
03722    }
03723 
03724    AST_LIST_UNLOCK(&queues);
03725 
03726    return res;
03727 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

Definition at line 541 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().

00542 {
00543    AST_LIST_LOCK(&queues);
00544    if (AST_LIST_REMOVE(&queues, q, list)) {
00545       ao2_ref(q, -1);
00546    }
00547    AST_LIST_UNLOCK(&queues);
00548 }

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 1959 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, callattempt::lastqueue, queue_ent::linpos, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, call_queue::ringinuse, call_queue::rrpos, ast_cdr::src, member::state_interface, member::status, callattempt::stillgoing, update_status(), ast_cdr::userfield, vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

01960 {
01961    int res;
01962    int status;
01963    char tech[256];
01964    char *location, *location2;
01965    const char *macrocontext, *macroexten;
01966    char pickupmark[256], chan[128];
01967 
01968    /* on entry here, we know that tmp->chan == NULL */
01969         if ((tmp->lastqueue && tmp->lastqueue->wrapuptime && (time(NULL) - tmp->lastcall < tmp->lastqueue->wrapuptime)) ||
01970                 (!tmp->lastqueue && qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime))) {
01971                 if (queue_debug)
01972          ast_log(LOG_NOTICE, "Wrapuptime not yet expired on queue %s for %s\n", 
01973             (tmp->lastqueue ? tmp->lastqueue->name : qe->parent->name), tmp->interface);
01974       tmp->stillgoing = 0;
01975       (*busies)++;
01976       return 0;
01977    }
01978 
01979    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01980       if (queue_debug)
01981          ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01982       tmp->stillgoing = 0;
01983       (*busies)++;
01984       return 0;
01985    }
01986 
01987    if (tmp->member->paused) {
01988       if (queue_debug)
01989          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01990       tmp->stillgoing = 0;
01991       (*busies)++;
01992       return 0;
01993    }
01994    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01995       if (queue_debug)
01996          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01997       if (qe->chan->cdr)
01998          ast_cdr_busy(qe->chan->cdr);
01999       tmp->stillgoing = 0;
02000       (*busies)++;
02001       return 0;
02002    }
02003 
02004    ast_copy_string(tech, tmp->interface, sizeof(tech));
02005    if ((location = strchr(tech, '/')))
02006       *location++ = '\0';
02007    else
02008       location = "";
02009 
02010    /* Request the peer */
02011    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
02012    if (!tmp->chan) {       /* If we can't, just go on to the next call */
02013       if (queue_debug)
02014          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
02015       if (qe->chan->cdr)
02016          ast_cdr_busy(qe->chan->cdr);
02017       tmp->stillgoing = 0;
02018 
02019       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02020 
02021       ao2_lock(qe->parent);
02022       qe->parent->rrpos++;
02023                 qe->linpos++;
02024       ao2_unlock(qe->parent);
02025 
02026       (*busies)++;
02027       return 0;
02028    }
02029    
02030    /* Increment ring count */
02031    tmp->member->ringcount++;
02032    tmp->chan->appl = "AppQueue";
02033    tmp->chan->data = "(Outgoing Line)";
02034    tmp->chan->whentohangup = 0;
02035    if (tmp->chan->cid.cid_num)
02036       free(tmp->chan->cid.cid_num);
02037    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
02038    if (tmp->chan->cid.cid_name)
02039       free(tmp->chan->cid.cid_name);
02040    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
02041    if (tmp->chan->cid.cid_ani)
02042       free(tmp->chan->cid.cid_ani);
02043    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
02044 
02045    /* Inherit specially named variables from parent channel */
02046    ast_channel_inherit_variables(qe->chan, tmp->chan);
02047    ast_channel_datastore_inherit(qe->chan, tmp->chan);
02048 
02049    /* Presense of ADSI CPE on outgoing channel follows ours */
02050    tmp->chan->adsicpe = qe->chan->adsicpe;
02051 
02052    /* Inherit context and extension */
02053    ast_channel_lock(qe->chan);
02054    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02055    if (!ast_strlen_zero(macrocontext))
02056       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
02057    else
02058       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
02059    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02060    if (!ast_strlen_zero(macroexten))
02061       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02062    else
02063       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02064    if (ast_cdr_isset_unanswered()) {
02065       /* they want to see the unanswered dial attempts! */
02066       /* set up the CDR fields on all the CDRs to give sensical information */
02067       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02068       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02069       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02070       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02071       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02072       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02073       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02074       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02075       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02076       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02077       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02078    }
02079    ast_channel_unlock(qe->chan);
02080 
02081    /* Add a PICKUPMARK variable to ringing interface */
02082    if (option_debug > 2)
02083       ast_log(LOG_DEBUG, "chan %s, tech: %s, part %s\n", tmp->chan->name, tech, location);
02084    /* Delete DAHDI ring pattern in tech like DAHDI/1r2 */
02085    ast_copy_string(chan, location, sizeof(chan));
02086    if (!strncasecmp(tech, "dahdi", 5)) {
02087       if((chan[0] > '0') && (chan[0] <= '9')) {
02088          if ((location2 = strchr(chan, 'r')))
02089             *location2++ = '\0';
02090       }
02091    }
02092    snprintf(pickupmark, sizeof(pickupmark), "%s/%s", tech, chan);
02093    pbx_builtin_setvar_helper(tmp->chan, "PICKUPMARK", pickupmark);
02094 
02095    /* Place the call, but don't wait on the answer */
02096    if ((res = ast_call(tmp->chan, location, 0))) {
02097       /* Again, keep going even if there's an error */
02098       if (option_debug)
02099          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
02100       if (option_verbose > 2)
02101          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
02102       do_hang(tmp);
02103       (*busies)++;
02104       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02105       return 0;
02106    } else if (qe->parent->eventwhencalled) {
02107       char vars[2048];
02108 
02109       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02110                "AgentCalled: %s\r\n"
02111                "AgentName: %s\r\n"
02112                "ChannelCalling: %s\r\n"
02113                "CallerID: %s\r\n"
02114                "CallerIDName: %s\r\n"
02115                "Context: %s\r\n"
02116                "Extension: %s\r\n"
02117                "Priority: %d\r\n"
02118                "%s",
02119                tmp->interface, tmp->member->membername, qe->chan->name,
02120                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02121                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02122                qe->chan->context, qe->chan->exten, qe->chan->priority,
02123                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02124       if (option_verbose > 2)
02125          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
02126    }
02127 
02128    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02129    return 1;
02130 }

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 2156 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().

02157 {
02158    int ret = 0;
02159 
02160    while (ret == 0) {
02161       struct callattempt *best = find_best(outgoing);
02162       if (!best) {
02163          if (option_debug)
02164             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02165          break;
02166       }
02167       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02168          struct callattempt *cur;
02169          /* Ring everyone who shares this best metric (for ringall) */
02170          for (cur = outgoing; cur; cur = cur->q_next) {
02171             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02172                if (option_debug)
02173                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02174                ret |= ring_entry(qe, cur, busies);
02175             }
02176          }
02177       } else {
02178          /* Ring just the best channel */
02179          if (option_debug)
02180             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02181          ret = ring_entry(qe, best, busies);
02182       }
02183    }
02184 
02185    return ret;
02186 }

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 2294 of file app_queue.c.

References ast_indicate(), ast_moh_start(), ast_queue_log(), ast_verbose(), call_queue::autopause, queue_ent::chan, queue_ent::moh, call_queue::name, option_verbose, queue_ent::parent, queue_ent::ring_when_ringing, set_member_paused(), and VERBOSE_PREFIX_3.

Referenced by wait_for_answer().

02295 {
02296    if (option_verbose > 2)
02297       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02298 
02299    /* Stop ringing, and resume MOH if specified */
02300    if (qe->ring_when_ringing) {
02301       ast_indicate(qe->chan, -1);
02302       ast_moh_start(qe->chan, qe->moh, NULL);
02303    }
02304 
02305    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02306    if (qe->parent->autopause && pause) {
02307       if (!set_member_paused(qe->parent->name, interface, 1)) {
02308          if (option_verbose > 2)
02309             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02310       } else {
02311          if (option_verbose > 2)
02312             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02313       }
02314    }
02315    return;
02316 }

static int rqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 4044 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_channel::context, ast_channel::exten, member_interface::interface, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

04045 {
04046    int res=-1;
04047    struct ast_module_user *lu;
04048    char *parse, *temppos = NULL;
04049    int priority_jump = 0;
04050    AST_DECLARE_APP_ARGS(args,
04051       AST_APP_ARG(queuename);
04052       AST_APP_ARG(interface);
04053       AST_APP_ARG(options);
04054    );
04055 
04056 
04057    if (ast_strlen_zero(data)) {
04058       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
04059       return -1;
04060    }
04061 
04062    parse = ast_strdupa(data);
04063 
04064    AST_STANDARD_APP_ARGS(args, parse);
04065 
04066    lu = ast_module_user_add(chan);
04067 
04068    if (ast_strlen_zero(args.interface)) {
04069       args.interface = ast_strdupa(chan->name);
04070       temppos = strrchr(args.interface, '-');
04071       if (temppos)
04072          *temppos = '\0';
04073    }
04074 
04075    if (args.options) {
04076       if (strchr(args.options, 'j'))
04077          priority_jump = 1;
04078    }
04079 
04080    switch (remove_from_queue(args.queuename, args.interface)) {
04081    case RES_OKAY:
04082       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
04083       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
04084       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
04085       res = 0;
04086       break;
04087    case RES_EXISTS:
04088       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
04089       if (priority_jump || ast_opt_priority_jumping)
04090          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04091       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
04092       res = 0;
04093       break;
04094    case RES_NOSUCHQUEUE:
04095       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
04096       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
04097       res = 0;
04098       break;
04099    case RES_NOT_DYNAMIC:
04100       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
04101       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
04102       res = 0;
04103       break;
04104    }
04105 
04106    ast_module_user_remove(lu);
04107 
04108    return res;
04109 }

static void rr_dep_warning ( void   )  [static]

Definition at line 483 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by find_queue_by_name_rt(), and reload_queues().

00484 {
00485    static unsigned int warned = 0;
00486 
00487    if (!warned) {
00488       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00489       warned = 1;
00490    }
00491 }

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 1195 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().

01196 {
01197    struct member *m, tmpmem;
01198    int penalty = 0;
01199    int paused  = 0;
01200 
01201    if (penalty_str) {
01202       penalty = atoi(penalty_str);
01203       if (penalty < 0)
01204          penalty = 0;
01205    }
01206 
01207    if (paused_str) {
01208       paused = atoi(paused_str);
01209       if (paused < 0)
01210          paused = 0;
01211    }
01212 
01213    /* Find the member, or the place to put a new one. */
01214    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01215    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01216 
01217    /* Create a new one if not found, else update penalty */
01218    if (!m) {
01219       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01220          m->dead = 0;
01221          m->realtime = 1;
01222          add_to_interfaces(m->state_interface);
01223          ao2_link(q->members, m);
01224          ao2_ref(m, -1);
01225          m = NULL;
01226          q->membercount++;
01227       }
01228    } else {
01229       m->dead = 0;   /* Do not delete this one. */
01230       if (paused_str)
01231          m->paused = paused;
01232       if (strcasecmp(state_interface, m->state_interface)) {
01233          remove_from_interfaces(m->state_interface);
01234          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01235          add_to_interfaces(m->state_interface);
01236       }
01237       m->penalty = penalty;
01238       ao2_ref(m, -1);
01239    }
01240 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

Definition at line 2236 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().

02237 {
02238    int res = 0;
02239    time_t now;
02240 
02241    /* Get the current time */
02242    time(&now);
02243 
02244    /* Check to see if it is time to announce */
02245    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02246       return 0;
02247 
02248    /* Stop the music on hold so we can play our own file */
02249    ast_moh_stop(qe->chan);
02250 
02251    if (option_verbose > 2)
02252       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02253 
02254    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02255    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02256       qe->last_periodic_announce_sound = 0;
02257    }
02258    
02259    /* play the announcement */
02260    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02261 
02262    if (res > 0 && !valid_exit(qe, res))
02263       res = 0;
02264 
02265    /* Resume Music on Hold if the caller is going to stay in the queue */
02266    if (!res)
02267       ast_moh_start(qe->chan, qe->moh, NULL);
02268 
02269    /* update last_periodic_announce_time */
02270    qe->last_periodic_announce_time = now;
02271 
02272    /* Update the current periodic announcement to the next announcement */
02273    qe->last_periodic_announce_sound++;
02274    
02275    return res;
02276 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1650 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, AST_DIGIT_ANY, ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verbose(), queue_ent::chan, call_queue::holdtime, queue_ent::last_pos, queue_ent::last_pos_said, queue_ent::moh, call_queue::name, option_verbose, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_minute, 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().

01651 {
01652    int res = 0, avgholdmins, avgholdsecs;
01653    time_t now;
01654 
01655    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01656    time(&now);
01657    if ((now - qe->last_pos) < 15)
01658       return 0;
01659 
01660    /* If either our position has changed, or we are over the freq timer, say position */
01661    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01662       return 0;
01663 
01664    ast_moh_stop(qe->chan);
01665    /* Say we're next, if we are */
01666    if (qe->pos == 1) {
01667       res = play_file(qe->chan, qe->parent->sound_next);
01668       if (res)
01669          goto playout;
01670       else
01671          goto posout;
01672    } else {
01673       res = play_file(qe->chan, qe->parent->sound_thereare);
01674       if (res)
01675          goto playout;
01676       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01677       if (res)
01678          goto playout;
01679       res = play_file(qe->chan, qe->parent->sound_calls);
01680       if (res)
01681          goto playout;
01682    }
01683    /* Round hold time to nearest minute */
01684    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01685 
01686    /* If they have specified a rounding then round the seconds as well */
01687    if (qe->parent->roundingseconds) {
01688       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01689       avgholdsecs *= qe->parent->roundingseconds;
01690    } else {
01691       avgholdsecs = 0;
01692    }
01693 
01694    if (option_verbose > 2)
01695       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minute(s) %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01696 
01697    /* If the hold time is >1 min, if it's enabled, and if it's not
01698       supposed to be only once and we have already said it, say it */
01699     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01700         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01701         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01702       res = play_file(qe->chan, qe->parent->sound_holdtime);
01703       if (res)
01704          goto playout;
01705 
01706       if (avgholdmins >= 1) {
01707          res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01708          if (res)
01709             goto playout;
01710          
01711          if (avgholdmins == 1) {
01712             res = play_file(qe->chan, qe->parent->sound_minute);
01713             if (res)
01714                goto playout;
01715          } else {
01716             res = play_file(qe->chan, qe->parent->sound_minutes);
01717             if (res)
01718                goto playout;
01719          }
01720          
01721       }
01722       if (avgholdsecs >= 1) {
01723          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01724          if (res)
01725             goto playout;
01726 
01727          res = play_file(qe->chan, qe->parent->sound_seconds);
01728          if (res)
01729             goto playout;
01730       }
01731 
01732    }
01733 
01734 posout:
01735    if (option_verbose > 2)
01736       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01737          qe->chan->name, qe->parent->name, qe->pos);
01738    res = play_file(qe->chan, qe->parent->sound_thanks);
01739 
01740 playout:
01741 
01742    if ((res > 0 && !valid_exit(qe, res)))
01743       res = 0;
01744 
01745    /* Set our last_pos indicators */
01746    qe->last_pos = now;
01747    qe->last_pos_said = qe->pos;
01748 
01749    /* Don't restart music on hold if we're about to exit the caller from the queue */
01750    if (!res)
01751       ast_moh_start(qe->chan, qe->moh, NULL);
01752 
01753    return res;
01754 }

static int set_member_paused ( const char *  queuename,
const char *  interface,
int  paused 
) [static]

Definition at line 3785 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().

03786 {
03787    int found = 0;
03788    struct call_queue *q;
03789    struct member *mem;
03790 
03791    /* Special event for when all queues are paused - individual events still generated */
03792    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03793    if (ast_strlen_zero(queuename))
03794       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03795 
03796    AST_LIST_LOCK(&queues);
03797    AST_LIST_TRAVERSE(&queues, q, list) {
03798       ao2_lock(q);
03799       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03800          if ((mem = interface_exists(q, interface))) {
03801             found++;
03802             if (mem->paused == paused)
03803                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03804             mem->paused = paused;
03805 
03806             if (queue_persistent_members)
03807                dump_queue_members(q);
03808 
03809             if (mem->realtime)
03810                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03811 
03812             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03813 
03814             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03815                "Queue: %s\r\n"
03816                "Location: %s\r\n"
03817                "MemberName: %s\r\n"
03818                "Paused: %d\r\n",
03819                   q->name, mem->interface, mem->membername, paused);
03820             ao2_ref(mem, -1);
03821          }
03822       }
03823       ao2_unlock(q);
03824    }
03825    AST_LIST_UNLOCK(&queues);
03826 
03827    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03828 }

static void set_queue_result ( struct ast_channel chan,
enum queue_result  res 
) [static]

sets the QUEUESTATUS channel variable

Definition at line 502 of file app_queue.c.

References pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00503 {
00504    int i;
00505 
00506    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00507       if (queue_results[i].id == res) {
00508          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00509          return;
00510       }
00511    }
00512 }

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 2927 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().

02928 {
02929    struct ast_datastore *ds;
02930    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02931 
02932    if (!qtds) {
02933       ast_log(LOG_WARNING, "Memory allocation error!\n");
02934       return NULL;
02935    }
02936 
02937    ast_channel_lock(qe->chan);
02938    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02939       ast_channel_unlock(qe->chan);
02940       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02941       return NULL;
02942    }
02943 
02944    qtds->qe = qe;
02945    /* This member is refcounted in try_calling, so no need to add it here, too */
02946    qtds->member = member;
02947    qtds->starttime = starttime;
02948    qtds->callcompletedinsl = callcompletedinsl;
02949    ds->data = qtds;
02950    ast_channel_datastore_add(qe->chan, ds);
02951    ast_channel_unlock(qe->chan);
02952    return ds;
02953 }

static int statechange_queue ( const char *  dev,
int  state,
void *  ign 
) [static]

Producer of the statechange queue.

Definition at line 799 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().

00800 {
00801    struct statechange *sc;
00802 
00803    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00804       return 0;
00805 
00806    sc->state = state;
00807    strcpy(sc->dev, dev);
00808 
00809    ast_mutex_lock(&device_state.lock);
00810    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00811    ast_cond_signal(&device_state.cond);
00812    ast_mutex_unlock(&device_state.lock);
00813 
00814    return 0;
00815 }

static int store_next_lin ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Definition at line 2212 of file app_queue.c.

References ast_log(), find_best(), callattempt::interface, queue_ent::linpos, queue_ent::linwrapped, LOG_DEBUG, callattempt::metric, and option_debug.

Referenced by try_calling().

02213 {
02214    struct callattempt *best = find_best(outgoing);
02215 
02216    if (best) {
02217       /* Ring just the best channel */
02218       if (option_debug)
02219          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02220       qe->linpos = best->metric % 1000;
02221    } else {
02222       /* Just increment rrpos */
02223       if (qe->linwrapped) {
02224          /* No more channels, start over */
02225          qe->linpos = 0;
02226       } else {
02227          /* Prioritize next entry */
02228          qe->linpos++;
02229       }
02230    }
02231    qe->linwrapped = 0;
02232 
02233    return 0;
02234 }

static int store_next_rr ( struct queue_ent qe,
struct callattempt outgoing 
) [static]

Definition at line 2188 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().

02189 {
02190    struct callattempt *best = find_best(outgoing);
02191 
02192    if (best) {
02193       /* Ring just the best channel */
02194       if (option_debug)
02195          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02196       qe->parent->rrpos = best->metric % 1000;
02197    } else {
02198       /* Just increment rrpos */
02199       if (qe->parent->wrapped) {
02200          /* No more channels, start over */
02201          qe->parent->rrpos = 0;
02202       } else {
02203          /* Prioritize next entry */
02204          qe->parent->rrpos++;
02205       }
02206    }
02207    qe->parent->wrapped = 0;
02208 
02209    return 0;
02210 }

static int strat2int ( const char *  strategy  )  [static]

Definition at line 526 of file app_queue.c.

References name, and strategies.

Referenced by find_queue_by_name_rt(), queue_set_param(), and reload_queues().

00527 {
00528    int x;
00529 
00530    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00531       if (!strcasecmp(strategy, strategies[x].name))
00532          return strategies[x].strategy;
00533    }
00534 
00535    return -1;
00536 }

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.

Parameters:
[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 2980 of file app_queue.c.

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, ast_cdr_failed(), AST_CDR_FLAG_DONT_TOUCH, AST_CDR_FLAG_LOCKED, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_cdr_noanswer(), ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_clear_flag, ast_copy_string(), AST_DEVICE_NOT_INUSE, AST_DIGIT_ANY, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, ast_hangup(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), AST_OPTION_TONE_VERIFY, ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, attended_transfer_occurred(), calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, ast_bridge_config::features_callee, ast_bridge_config::features_caller, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, callattempt::interface, ast_dialed_interface::interface, member::interface, member::lastcall, callattempt::lastcall, member::lastqueue, callattempt::lastqueue, leave_queue(), LOG_DEBUG, LOG_ERROR, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, callattempt::oldstatus, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), queue_ent::pending, play_file(), queue_ent::pos, callattempt::q_next, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_LINEAR, QUEUE_STRATEGY_ROUNDROBIN, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, queue_transfer_info, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, call_queue::servicelevel, call_queue::setinterfacevar, setup_transfer_datastore(), call_queue::sound_minute, call_queue::sound_minutes, call_queue::sound_reporthold, call_queue::sound_seconds, queue_ent::start, member::status, callattempt::stillgoing, store_next_lin(), store_next_rr(), call_queue::strategy, tds, ast_channel::tech, call_queue::timeout, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), and wait_for_answer().

Referenced by queue_exec().

02981 {
02982    struct member *cur;
02983    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02984    int to;
02985    char oldexten[AST_MAX_EXTENSION]="";
02986    char oldcontext[AST_MAX_CONTEXT]="";
02987    char queuename[256]="";
02988    struct ast_channel *peer;
02989    struct ast_channel *which;
02990    struct callattempt *lpeer;
02991    struct member *member;
02992    struct ast_app *app;
02993    int res = 0, bridge = 0;
02994    int numbusies = 0;
02995    int x=0;
02996    char *announce = NULL;
02997    char digit = 0;
02998    time_t callstart;
02999    time_t now = time(NULL);
03000    struct ast_bridge_config bridge_config;
03001    char nondataquality = 1;
03002    char *agiexec = NULL;
03003    int ret = 0;
03004    const char *monitorfilename;
03005    const char *monitor_exec;
03006    const char *monitor_options;
03007    char tmpid[256], tmpid2[256];
03008    char meid[1024], meid2[1024];
03009    char mixmonargs[1512];
03010    struct ast_app *mixmonapp = NULL;
03011    char *p;
03012    char vars[2048];
03013    int forwardsallowed = 1;
03014    int callcompletedinsl;
03015    struct ao2_iterator memi;
03016    struct ast_datastore *datastore, *transfer_ds;
03017    const int need_weight = use_weight;
03018 
03019    ast_channel_lock(qe->chan);
03020    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
03021    ast_channel_unlock(qe->chan);
03022 
03023    memset(&bridge_config, 0, sizeof(bridge_config));
03024    time(&now);
03025 
03026    /* If we've already exceeded our timeout, then just stop
03027     * This should be extremely rare. queue_exec will take care
03028     * of removing the caller and reporting the timeout as the reason.
03029     */
03030    if (qe->expire && now >= qe->expire) {
03031       res = 0;
03032       goto out;
03033    }
03034       
03035    for (; options && *options; options++)
03036       switch (*options) {
03037       case 't':
03038          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
03039          break;
03040       case 'T':
03041          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
03042          break;
03043       case 'w':
03044          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
03045          break;
03046       case 'W':
03047          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
03048          break;
03049       case 'd':
03050          nondataquality = 0;
03051          break;
03052       case 'h':
03053          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
03054          break;
03055       case 'H':
03056          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
03057          break;
03058       case 'n':
03059          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED || qe->parent->strategy == QUEUE_STRATEGY_LINEAR)
03060             (*tries)++;
03061          else
03062             *tries = qe->parent->membercount;
03063          *noption = 1;
03064          break;
03065       case 'i':
03066          forwardsallowed = 0;
03067          break;
03068       }
03069 
03070    /* Hold the lock while we setup the outgoing calls */
03071 
03072    if (need_weight)
03073       AST_LIST_LOCK(&queues);
03074    ao2_lock(qe->parent);
03075    if (option_debug)
03076       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
03077                      qe->chan->name);
03078    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
03079    if (!ast_strlen_zero(qe->announce))
03080       announce = qe->announce;
03081    if (!ast_strlen_zero(announceoverride))
03082       announce = announceoverride;
03083 
03084    memi = ao2_iterator_init(qe->parent->members, 0);
03085    while ((cur = ao2_iterator_next(&memi))) {
03086       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
03087       struct ast_dialed_interface *di;
03088       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
03089       if (!tmp) {
03090          ao2_iterator_destroy(&memi);
03091          ao2_ref(cur, -1);
03092          ao2_unlock(qe->parent);
03093          if (need_weight)
03094             AST_LIST_UNLOCK(&queues);
03095          goto out;
03096       }
03097       if (!datastore) {
03098          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
03099             ao2_iterator_destroy(&memi);
03100             ao2_ref(cur, -1);
03101             ao2_unlock(qe->parent);
03102             if (need_weight)
03103                AST_LIST_UNLOCK(&queues);
03104             free(tmp);
03105             goto out;
03106          }
03107          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
03108          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
03109             ao2_iterator_destroy(&memi);
03110             ao2_ref(cur, -1);
03111             ao2_unlock(qe->parent);
03112             if (need_weight)
03113                AST_LIST_UNLOCK(&queues);
03114             free(tmp);
03115             goto out;
03116          }
03117          datastore->data = dialed_interfaces;
03118          AST_LIST_HEAD_INIT(dialed_interfaces);
03119 
03120          ast_channel_lock(qe->chan);
03121          ast_channel_datastore_add(qe->chan, datastore);
03122          ast_channel_unlock(qe->chan);
03123       } else
03124          dialed_interfaces = datastore->data;
03125 
03126       AST_LIST_LOCK(dialed_interfaces);
03127       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
03128          if (!strcasecmp(cur->interface, di->interface)) {
03129             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
03130                di->interface);
03131             break;
03132          }
03133       }
03134       AST_LIST_UNLOCK(dialed_interfaces);
03135       
03136       if (di) {
03137          free(tmp);
03138          continue;
03139       }
03140 
03141       /* It is always ok to dial a Local interface.  We only keep track of
03142        * which "real" interfaces have been dialed.  The Local channel will
03143        * inherit this list so that if it ends up dialing a real interface,
03144        * it won't call one that has already been called. */
03145       if (strncasecmp(cur->interface, "Local/", 6)) {
03146          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
03147             ao2_iterator_destroy(&memi);
03148             ao2_ref(cur, -1);
03149             ao2_unlock(qe->parent);
03150             if (need_weight)
03151                AST_LIST_UNLOCK(&queues);
03152             free(tmp);
03153             goto out;
03154          }
03155          strcpy(di->interface, cur->interface);
03156 
03157          AST_LIST_LOCK(dialed_interfaces);
03158          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03159          AST_LIST_UNLOCK(dialed_interfaces);
03160       }
03161 
03162       tmp->stillgoing = -1;
03163       tmp->member = cur;
03164       tmp->oldstatus = cur->status;
03165       tmp->lastcall = cur->lastcall;
03166       tmp->lastqueue = cur->lastqueue;
03167       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03168       if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
03169          cur->ringcount = 0;
03170       }
03171       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03172          just calculate their metric for the appropriate strategy */
03173       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03174          /* Put them in the list of outgoing thingies...  We're ready now.
03175             XXX If we're forcibly removed, these outgoing calls won't get
03176             hung up XXX */
03177          tmp->q_next = outgoing;
03178          outgoing = tmp;      
03179          /* If this line is up, don't try anybody else */
03180          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03181             break;
03182       } else {
03183          ao2_ref(cur, -1);
03184          free(tmp);
03185       }
03186    }
03187    ao2_iterator_destroy(&memi);
03188    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03189       to = (qe->expire - now) * 1000;
03190    else
03191       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03192    ++qe->pending;
03193    ++qe->tries;
03194    if (option_debug)
03195       ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
03196                   qe->chan->name, queuename, qe->tries);
03197    ao2_unlock(qe->parent);
03198    ring_one(qe, outgoing, &numbusies);
03199    if (need_weight)
03200       AST_LIST_UNLOCK(&queues);
03201    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03202    /* The ast_channel_datastore_remove() function could fail here if the
03203     * datastore was moved to another channel during a masquerade. If this is
03204     * the case, don't free the datastore here because later, when the channel
03205     * to which the datastore was moved hangs up, it will attempt to free this
03206     * datastore again, causing a crash
03207     */
03208    ast_channel_lock(qe->chan);
03209    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03210       ast_channel_datastore_free(datastore);
03211    }
03212    ast_channel_unlock(qe->chan);
03213    ao2_lock(qe->parent);
03214    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY || qe->parent->strategy == QUEUE_STRATEGY_RRORDERED) {
03215       store_next_rr(qe, outgoing);
03216    }
03217         if (qe->parent->strategy == QUEUE_STRATEGY_LINEAR) {
03218      store_next_lin(qe, outgoing);
03219         }
03220    ao2_unlock(qe->parent);
03221    peer = lpeer ? lpeer->chan : NULL;
03222    if (!peer) {
03223       qe->pending = 0;
03224       if (to) {
03225          /* Must gotten hung up */
03226          res = -1;
03227       } else {
03228          /* User exited by pressing a digit */
03229          res = digit;
03230       }
03231       if (res == -1) {
03232          /* Post this CDR, and mark call as NOANSWER */
03233          ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH);
03234          ast_cdr_noanswer(qe->chan->cdr);
03235          if (queue_debug)
03236             ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
03237       }
03238       if (qe->parent->eventwhencalled) {
03239          manager_event(EVENT_FLAG_AGENT, "AgentTimeout",
03240                               "Queue: %s\r\n"
03241                               "ChannelCalling: %s\r\n"
03242                               "Uniqueid: %s\r\n"
03243                               "Tries: %d\r\n"
03244                               "Holdtime: %ld\r\n",
03245                               queuename, qe->chan->name, qe->chan->uniqueid, qe->tries,
03246                               (long)time(NULL) - qe->start);
03247       }
03248       if (ast_cdr_isset_unanswered()) {
03249          /* channel contains the name of one of the outgoing channels
03250             in its CDR; zero out this CDR to avoid a dual-posting */
03251          struct callattempt *o;
03252          for (o = outgoing; o; o = o->q_next) {
03253             if (!o->chan) {
03254                continue;
03255             }
03256             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03257                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03258                break;
03259             }
03260          }
03261       }
03262    } else { /* peer is valid */
03263       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03264          we will always return with -1 so that it is hung up properly after the
03265          conversation.  */
03266       if (!strcmp(qe->chan->tech->type, "Zap"))
03267          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03268       if (!strcmp(peer->tech->type, "Zap"))
03269          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03270       /* Update parameters for the queue */
03271       time(&now);
03272       recalc_holdtime(qe, (now - qe->start));
03273       ao2_lock(qe->parent);
03274       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03275       ao2_unlock(qe->parent);
03276       member = lpeer->member;
03277       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03278       ao2_ref(member, 1);
03279       hangupcalls(outgoing, peer);
03280       outgoing = NULL;
03281       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03282          int res2;
03283 
03284          res2 = ast_autoservice_start(qe->chan);
03285          if (!res2) {
03286             if (qe->parent->memberdelay) {
03287                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03288                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03289             }
03290             if (!res2 && announce) {
03291                if (play_file(peer, announce) < 0) {
03292                   ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", announce, peer->name);
03293                }
03294             }
03295             if (!res2 && qe->parent->reportholdtime) {
03296                if (!play_file(peer, qe->parent->sound_reporthold)) {
03297                   int holdtime, holdtimesecs;
03298 
03299                   time(&now);
03300                   holdtime = abs((now - qe->start) / 60);
03301                   holdtimesecs = abs((now - qe->start) % 60);
03302                   if (holdtime == 1) {
03303                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03304                      if (play_file(peer, qe->parent->sound_minute) < 0) {
03305                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minute, peer->name);
03306                      }
03307                   } else {
03308                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03309                      if (play_file(peer, qe->parent->sound_minutes) < 0) {
03310                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_minutes, peer->name);
03311                      }
03312                   }
03313                   if (holdtimesecs > 1) {
03314                      ast_say_number(peer, holdtimesecs, AST_DIGIT_ANY, peer->language, NULL);
03315                      if (play_file(peer, qe->parent->sound_seconds) < 0) {
03316                         ast_log(LOG_ERROR, "play_file failed for '%s' on %s\n", qe->parent->sound_seconds, peer->name);
03317                      }
03318                   }
03319                }
03320             }
03321          }
03322          res2 |= ast_autoservice_stop(qe->chan);
03323          if (peer->_softhangup) {
03324             /* Agent must have hung up */
03325             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03326             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03327             if (qe->parent->eventwhencalled)
03328                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03329                      "Queue: %s\r\n"
03330                      "Uniqueid: %s\r\n"
03331                      "Channel: %s\r\n"
03332                      "Member: %s\r\n"
03333                      "MemberName: %s\r\n"
03334                      "%s",
03335                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03336                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03337             ast_hangup(peer);
03338             ao2_ref(member, -1);
03339             goto out;
03340          } else if (res2) {
03341             /* Caller must have hung up just before being connected*/
03342             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03343             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
03344             record_abandoned(qe);
03345             ast_hangup(peer);
03346             ao2_ref(member, -1);
03347             return -1;
03348          }
03349       }
03350       /* Stop music on hold */
03351       ast_moh_stop(qe->chan);
03352       /* If appropriate, log that we have a destination channel */
03353       if (qe->chan->cdr)
03354          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03355       /* Make sure channels are compatible */
03356       res = ast_channel_make_compatible(qe->chan, peer);
03357       if (res < 0) {
03358          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03359          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03360          record_abandoned(qe);
03361          ast_cdr_failed(qe->chan->cdr);
03362          ast_hangup(peer);
03363          ao2_ref(member, -1);
03364          return -1;
03365       }
03366 
03367       if (qe->parent->setinterfacevar)
03368             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
03369 
03370       /* Begin Monitoring */
03371       if (qe->parent->monfmt && *qe->parent->monfmt) {
03372          if (!qe->parent->montype) {
03373             if (option_debug)
03374                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
03375             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03376             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
03377                which = qe->chan;
03378             else
03379                which = peer;
03380             if (monitorfilename)
03381                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
03382             else if (qe->chan->cdr)
03383                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
03384             else {
03385                /* Last ditch effort -- no CDR, make up something */
03386                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03387                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
03388             }
03389             if (qe->parent->monjoin)
03390                ast_monitor_setjoinfiles(which, 1);
03391          } else {
03392             if (option_debug)
03393                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
03394             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03395             if (!monitorfilename) {
03396                if (qe->chan->cdr)
03397                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
03398                else
03399                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03400             } else {
03401                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
03402                for (p = tmpid2; *p ; p++) {
03403                   if (*p == '^' && *(p+1) == '{') {
03404                      *p = '$';
03405                   }
03406                }
03407 
03408                memset(tmpid, 0, sizeof(tmpid));
03409                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03410             }
03411 
03412             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
03413             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
03414 
03415             if (monitor_exec) {
03416                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
03417                for (p = meid2; *p ; p++) {
03418                   if (*p == '^' && *(p+1) == '{') {
03419                      *p = '$';
03420                   }
03421                }
03422 
03423                memset(meid, 0, sizeof(meid));
03424                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03425             }
03426    
03427             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
03428 
03429             mixmonapp = pbx_findapp("MixMonitor");
03430 
03431             if (strchr(tmpid2, '|')) {
03432                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
03433                mixmonapp = NULL;
03434             }
03435 
03436             if (!monitor_options)
03437                monitor_options = "";
03438             
03439             if (strchr(monitor_options, '|')) {
03440                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
03441                mixmonapp = NULL;
03442             }
03443 
03444             if (mixmonapp) {
03445                if (!ast_strlen_zero(monitor_exec))
03446                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
03447                else
03448                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
03449                   
03450                if (option_debug)
03451                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03452                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03453                if (qe->chan->cdr)
03454                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03455                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03456                if (qe->chan->cdr)
03457                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03458 
03459             } else
03460                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03461 
03462          }
03463       }
03464       /* Drop out of the queue at this point, to prepare for next caller */
03465       leave_queue(qe);        
03466       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03467          if (option_debug)
03468             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
03469          ast_channel_sendurl(peer, url);
03470       }
03471       if (!ast_strlen_zero(agi)) {
03472          if (option_debug)
03473             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
03474          app = pbx_findapp("agi");
03475          if (app) {
03476             agiexec = ast_strdupa(agi);
03477             ret = pbx_exec(qe->chan, app, agiexec);
03478          } else
03479             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03480       }
03481       qe->handled++;
03482       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
03483       if (qe->parent->eventwhencalled)
03484          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03485                "Queue: %s\r\n"
03486                "Uniqueid: %s\r\n"
03487                "Channel: %s\r\n"
03488                "Member: %s\r\n"
03489                "MemberName: %s\r\n"
03490                "Holdtime: %ld\r\n"
03491                "BridgedChannel: %s\r\n"
03492                "%s",
03493                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03494                (long)time(NULL) - qe->start, peer->uniqueid,
03495                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03496       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03497       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03498       time(&callstart);
03499 
03500       if (member->status == AST_DEVICE_NOT_INUSE)
03501          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);
03502          
03503       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03504       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03505 
03506       ast_channel_lock(qe->chan);
03507       if (!attended_transfer_occurred(qe->chan)) {
03508          struct ast_datastore *tds;
03509 
03510          /* detect a blind transfer */
03511          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
03512             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03513                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03514                (long) (time(NULL) - callstart));
03515             if (qe->parent->eventwhencalled)
03516                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03517                      "Queue: %s\r\n"
03518                      "Uniqueid: %s\r\n"
03519                      "Channel: %s\r\n"
03520                      "Member: %s\r\n"
03521                      "MemberName: %s\r\n"
03522                      "HoldTime: %ld\r\n"
03523                      "TalkTime: %ld\r\n"
03524                      "Reason: transfer\r\n"
03525                      "%s",
03526                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03527                      (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03528                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03529          } else if (qe->chan->_softhangup) {
03530             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03531                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03532             if (qe->parent->eventwhencalled)
03533                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03534                      "Queue: %s\r\n"
03535                      "Uniqueid: %s\r\n"
03536                      "Channel: %s\r\n"
03537                      "Member: %s\r\n"
03538                      "MemberName: %s\r\n"
03539                      "HoldTime: %ld\r\n"
03540                      "TalkTime: %ld\r\n"
03541                      "Reason: caller\r\n"
03542                      "%s",
03543                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03544                      (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03545                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03546          } else {
03547             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03548                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03549             if (qe->parent->eventwhencalled)
03550                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03551                      "Queue: %s\r\n"
03552                      "Uniqueid: %s\r\n"
03553                      "Channel: %s\r\n"
03554                      "Member: %s\r\n"
03555                      "MemberName: %s\r\n"
03556                      "HoldTime: %ld\r\n"
03557                      "TalkTime: %ld\r\n"
03558                      "Reason: agent\r\n"
03559                      "%s",
03560                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start),
03561                      (long)(time(NULL) - callstart),
03562                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03563          }
03564          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
03565             ast_channel_datastore_remove(qe->chan, tds);
03566          }
03567          update_queue(qe->parent, member, callcompletedinsl);
03568       } else {
03569          if (qe->parent->eventwhencalled)
03570             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03571                   "Queue: %s\r\n"
03572                   "Uniqueid: %s\r\n"
03573                   "Channel: %s\r\n"
03574                   "Member: %s\r\n"
03575                   "MemberName: %s\r\n"
03576                   "HoldTime: %ld\r\n"
03577                   "TalkTime: %ld\r\n"
03578                   "Reason: transfer\r\n"
03579                   "%s",
03580                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername, (long)(callstart - qe->start),
03581                   (long)(time(NULL) - callstart),
03582                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03583       }
03584 
03585       if (transfer_ds) {
03586          ast_channel_datastore_free(transfer_ds);
03587       }
03588       ast_channel_unlock(qe->chan);
03589       ast_hangup(peer);
03590       res = bridge ? bridge : 1;
03591       ao2_ref(member, -1);
03592    }
03593 out:
03594    hangupcalls(outgoing, NULL);
03595 
03596    return res;
03597 }

static int unload_module ( void   )  [static]

Definition at line 5871 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, queueexists_function, queuemembercount_function, queuememberlist_function, queuememberpaused_function, queuememberstatus_function, queuewaitingcount_function, and statechange_queue().

05872 {
05873    int res;
05874 
05875    if (device_state.thread != AST_PTHREADT_NULL) {
05876       device_state.stop = 1;
05877       ast_mutex_lock(&device_state.lock);
05878       ast_cond_signal(&device_state.cond);
05879       ast_mutex_unlock(&device_state.lock);
05880       pthread_join(device_state.thread, NULL);
05881    }
05882 
05883    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05884    res = ast_manager_unregister("QueueStatus");
05885    res |= ast_manager_unregister("Queues");
05886    res |= ast_manager_unregister("QueueAdd");
05887    res |= ast_manager_unregister("QueueRemove");
05888    res |= ast_manager_unregister("QueuePause");
05889    res |= ast_unregister_application(app_aqm);
05890    res |= ast_unregister_application(app_rqm);
05891    res |= ast_unregister_application(app_pqm);
05892    res |= ast_unregister_application(app_upqm);
05893    res |= ast_unregister_application(app_ql);
05894    res |= ast_unregister_application(app);
05895    res |= ast_custom_function_unregister(&queueexists_function);
05896    res |= ast_custom_function_unregister(&queueagentcount_function);
05897    res |= ast_custom_function_unregister(&queuemembercount_function);
05898    res |= ast_custom_function_unregister(&queuememberlist_function);
05899    res |= ast_custom_function_unregister(&queuememberstatus_function);
05900    res |= ast_custom_function_unregister(&queuememberpaused_function);
05901    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05902    ast_devstate_del(statechange_queue, NULL);
05903 
05904    ast_module_user_hangup_all();
05905 
05906    clear_and_free_interfaces();
05907 
05908    return res;
05909 }

static int update_queue ( struct call_queue q,
struct member member,
int  callcompletedinsl 
) [static]

Definition at line 2746 of file app_queue.c.

References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, member::lastqueue, and call_queue::members.

Referenced by queue_transfer_fixup(), and try_calling().

02747 {
02748    struct member *mem;
02749    struct call_queue *qtmp;
02750 
02751    if (shared_lastcall) {
02752       AST_LIST_LOCK(&queues);
02753       AST_LIST_TRAVERSE(&queues, qtmp, list) {
02754          ao2_lock(qtmp);
02755          if ((mem = ao2_find(qtmp->members, member, OBJ_POINTER))) {
02756             time(&mem->lastcall);
02757             mem->calls++;
02758             mem->lastqueue = q;
02759             ao2_ref(mem, -1);
02760          }
02761          ao2_unlock(qtmp);
02762       }
02763       AST_LIST_UNLOCK(&queues);
02764    } else {
02765       ao2_lock(q);
02766       time(&member->lastcall);
02767       member->calls++;
02768       member->lastqueue = q;
02769       ao2_unlock(q);
02770    }  
02771    ao2_lock(q);
02772    q->callscompleted++;
02773    if (callcompletedinsl)
02774       q->callscompletedinsl++;
02775    ao2_unlock(q);
02776 
02777    return 0;
02778 }

static int update_realtime_member_field ( struct member mem,
const char *  queue_name,
const char *  field,
const char *  value 
) [static]

Definition at line 1408 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().

01409 {
01410    struct ast_variable *var, *save;
01411    int ret = -1;
01412 
01413    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01414       return ret;
01415    save = var;
01416    while (var) {
01417       if (!strcmp(var->name, "uniqueid"))
01418          break;
01419       var = var->next;
01420    }
01421    if (var && !ast_strlen_zero(var->value)) {
01422       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01423          ret = 0;
01424    }
01425    ast_variables_destroy(save);
01426    return ret;
01427 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1429 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().

01430 {
01431    struct ast_config *member_config = NULL;
01432    struct member *m;
01433    char *interface = NULL;
01434    struct ao2_iterator mem_iter;
01435 
01436    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01437       /*This queue doesn't have realtime members*/
01438       if (option_debug > 2)
01439          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01440       return;
01441    }
01442 
01443    ao2_lock(q);
01444    
01445    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01446    mem_iter = ao2_iterator_init(q->members, 0);
01447    while ((m = ao2_iterator_next(&mem_iter))) {
01448       if (m->realtime)
01449          m->dead = 1;
01450       ao2_ref(m, -1);
01451    }
01452    ao2_iterator_destroy(&mem_iter);
01453 
01454    while ((interface = ast_category_browse(member_config, interface))) {
01455       rt_handle_member_record(q, interface,
01456          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01457          ast_variable_retrieve(member_config, interface, "penalty"),
01458          ast_variable_retrieve(member_config, interface, "paused"),
01459          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01460    }
01461 
01462    /* Delete all realtime members that have been deleted in DB. */
01463    mem_iter = ao2_iterator_init(q->members, 0);
01464    while ((m = ao2_iterator_next(&mem_iter))) {
01465       if (m->dead) {
01466          ao2_unlink(q->members, m);
01467          ao2_unlock(q);
01468          remove_from_interfaces(m->state_interface);
01469          ao2_lock(q);
01470          q->membercount--;
01471       }
01472       ao2_ref(m, -1);
01473    }
01474    ao2_iterator_destroy(&mem_iter);
01475    ao2_unlock(q);
01476    ast_config_destroy(member_config);
01477 }

static int update_status ( const char *  interface,
const int  status 
) [static]

Definition at line 650 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().

00651 {
00652    struct member *cur;
00653    struct ao2_iterator mem_iter;
00654    struct call_queue *q;
00655    char tmp_interface[80];
00656 
00657    AST_LIST_LOCK(&queues);
00658    AST_LIST_TRAVERSE(&queues, q, list) {
00659       ao2_lock(q);
00660       mem_iter = ao2_iterator_init(q->members, 0);
00661       while ((cur = ao2_iterator_next(&mem_iter))) {
00662          char *slash_pos;
00663          ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
00664          if ((slash_pos = strchr(tmp_interface, '/')))
00665             if ((slash_pos = strchr(slash_pos + 1, '/')))
00666                *slash_pos = '\0';
00667 
00668          if (strcasecmp(interface, tmp_interface)) {
00669             ao2_ref(cur, -1);
00670             continue;
00671          }
00672 
00673          if (cur->status != status) {
00674             cur->status = status;
00675             if (q->maskmemberstatus) {
00676                ao2_ref(cur, -1);
00677                continue;
00678             }
00679 
00680             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00681                "Queue: %s\r\n"
00682                "Location: %s\r\n"
00683                "MemberName: %s\r\n"
00684                "Membership: %s\r\n"
00685                "Penalty: %d\r\n"
00686                "CallsTaken: %d\r\n"
00687                "LastCall: %d\r\n"
00688                "Status: %d\r\n"
00689                "Paused: %d\r\n",
00690                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00691                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00692          }
00693          ao2_ref(cur, -1);
00694       }
00695       ao2_iterator_destroy(&mem_iter);
00696       ao2_unlock(q);
00697    }
00698    AST_LIST_UNLOCK(&queues);
00699 
00700    return 0;
00701 }

static int upqm_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 3986 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().

03987 {
03988    struct ast_module_user *lu;
03989    char *parse;
03990    int priority_jump = 0;
03991    int ignore_fail = 0;
03992    AST_DECLARE_APP_ARGS(args,
03993       AST_APP_ARG(queuename);
03994       AST_APP_ARG(interface);
03995       AST_APP_ARG(options);
03996    );
03997 
03998    if (ast_strlen_zero(data)) {
03999       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
04000       return -1;
04001    }
04002 
04003    parse = ast_strdupa(data);
04004 
04005    AST_STANDARD_APP_ARGS(args, parse);
04006 
04007    lu = ast_module_user_add(chan);
04008 
04009    if (args.options) {
04010       if (strchr(args.options, 'j'))
04011          priority_jump = 1;
04012       if (strchr(args.options, 'i'))
04013          ignore_fail = 1;
04014    }
04015 
04016    if (ast_strlen_zero(args.interface)) {
04017       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
04018       ast_module_user_remove(lu);
04019       return -1;
04020    }
04021 
04022    if (set_member_paused(args.queuename, args.interface, 0)) {
04023       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
04024       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
04025       if (priority_jump || ast_opt_priority_jumping) {
04026          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
04027             ast_module_user_remove(lu);
04028             return 0;
04029          }
04030       }
04031       ast_module_user_remove(lu);
04032       if (ignore_fail) {
04033          return 0;
04034       } else {
04035          return -1;
04036       }
04037    }
04038 
04039    ast_module_user_remove(lu);
04040    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
04041    return 0;
04042 }

static int valid_exit ( struct queue_ent qe,
char  digit 
) [static]

Definition at line 1617 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().

01618 {
01619    int digitlen = strlen(qe->digits);
01620 
01621    /* Prevent possible buffer overflow */
01622    if (digitlen < sizeof(qe->digits) - 2) {
01623       qe->digits[digitlen] = digit;
01624       qe->digits[digitlen + 1] = '\0';
01625    } else {
01626       qe->digits[0] = '\0';
01627       return 0;
01628    }
01629 
01630    /* If there's no context to goto, short-circuit */
01631    if (ast_strlen_zero(qe->context))
01632       return 0;
01633 
01634    /* If the extension is bad, then reset the digits to blank */
01635    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01636       qe->digits[0] = '\0';
01637       return 0;
01638    }
01639 
01640    /* We have an exact match */
01641    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01642       qe->valid_digits = 1;
01643       /* Return 1 on a successful goto */
01644       return 1;
01645    }
01646 
01647    return 0;
01648 }

static char* vars2manager ( struct ast_channel chan,
char *  vars,
size_t  len 
) [static]

Definition at line 1919 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01920 {
01921    char *tmp = alloca(len);
01922 
01923    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01924       int i, j;
01925 
01926       /* convert "\n" to "\nVariable: " */
01927       strcpy(vars, "Variable: ");
01928 
01929       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01930          vars[j] = tmp[i];
01931 
01932          if (tmp[i + 1] == '\0')
01933             break;
01934          if (tmp[i] == '\n') {
01935             vars[j++] = '\r';
01936             vars[j++] = '\n';
01937 
01938             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01939             j += 9;
01940          }
01941       }
01942       if (j > len - 3)
01943          j = len - 3;
01944       vars[j++] = '\r';
01945       vars[j++] = '\n';
01946       vars[j] = '\0';
01947    } else {
01948       /* there are no channel variables; leave it blank */
01949       *vars = '\0';
01950    }
01951    return vars;
01952 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3599 of file app_queue.c.

References ast_waitfordigit(), queue_ent::chan, queue_ent::parent, call_queue::retry, and valid_exit().

Referenced by queue_exec().

03600 {
03601    /* Don't need to hold the lock while we setup the outgoing calls */
03602    int retrywait = qe->parent->retry * 1000;
03603 
03604    int res = ast_waitfordigit(qe->chan, retrywait);
03605    if (res > 0 && !valid_exit(qe, res))
03606       res = 0;
03607 
03608    return res;
03609 }

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.

Parameters:
[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 2329 of file app_queue.c.

References ast_channel::_state, accountcode, ast_call(), ast_cdr_busy(), ast_cdr_failed(), ast_cdr_noanswer(), ast_channel_datastore_inherit(), ast_channel_inherit_variables(), ast_check_hangup(), AST_CONTROL_ANSWER, AST_CONTROL_BUSY, AST_CONTROL_CONGESTION, AST_CONTROL_HANGUP, AST_CONTROL_OFFHOOK, AST_CONTROL_RINGING, ast_copy_string(), AST_FRAME_CONTROL, AST_FRAME_DTMF, ast_frfree, ast_hangup(), ast_indicate(), ast_log(), AST_MAX_WATCHERS, ast_moh_stop(), ast_read(), ast_request(), AST_STATE_UP, ast_strdup, ast_string_field_set, ast_strlen_zero(), ast_verbose(), ast_waitfor_n(), callattempt::call_next, ast_channel::cdr, ast_channel::cdrflags, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_callerid::cid_rdnis, ast_channel::context, do_hang(), ast_channel::exten, f, ast_frame::frametype, free, callattempt::interface, member::interface, LOG_DEBUG, LOG_NOTICE, ast_channel::macroexten, callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_verbose, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), queue_ent::ring_when_ringing, rna(), S_OR, starttime, callattempt::stillgoing, call_queue::strategy, ast_frame::subclass, ast_channel::tech, call_queue::timeoutrestart, valid_exit(), and VERBOSE_PREFIX_3.

Referenced by try_calling().

02330 {
02331    char *queue = qe->parent->name;
02332    struct callattempt *o, *start = NULL, *prev = NULL;
02333    int status;
02334    int numbusies = prebusies;
02335    int numnochan = 0;
02336    int stillgoing = 0;
02337    int orig = *to;
02338    struct ast_frame *f;
02339    struct callattempt *peer = NULL;
02340    struct ast_channel *winner;
02341    struct ast_channel *in = qe->chan;
02342    char on[80] = "";
02343    char membername[80] = "";
02344    long starttime = 0;
02345    long endtime = 0; 
02346 
02347    starttime = (long) time(NULL);
02348    
02349    while (*to && !peer) {
02350       int numlines, retry, pos = 1;
02351       struct ast_channel *watchers[AST_MAX_WATCHERS];
02352       watchers[0] = in;
02353       start = NULL;
02354 
02355       for (retry = 0; retry < 2; retry++) {
02356          numlines = 0;
02357          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02358             if (o->stillgoing) { /* Keep track of important channels */
02359                stillgoing = 1;
02360                if (o->chan) {
02361                   watchers[pos++] = o->chan;
02362                   if (!start)
02363                      start = o;
02364                   else
02365                      prev->call_next = o;
02366                   prev = o;
02367                }
02368             }
02369             numlines++;
02370          }
02371          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02372             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02373             break;
02374          /* On "ringall" strategy we only move to the next penalty level
02375             when *all* ringing phones are done in the current penalty level */
02376          ring_one(qe, outgoing, &numbusies);
02377          /* and retry... */
02378       }
02379       if (pos == 1 /* not found */) {
02380          if (numlines == (numbusies + numnochan)) {
02381             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02382             if (in->cdr && (in->_state != AST_STATE_UP)) {
02383                ast_cdr_busy(in->cdr);
02384             }
02385          } else {
02386             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02387             if (in->cdr && (in->_state != AST_STATE_UP)) {
02388                ast_cdr_failed(in->cdr);
02389             }
02390          }
02391          *to = 0;
02392          return NULL;
02393       }
02394 
02395       /* Poll for events from both the incoming channel as well as any outgoing channels */
02396       winner = ast_waitfor_n(watchers, pos, to);
02397 
02398       /* Service all of the outgoing channels */
02399       for (o = start; o; o = o->call_next) {
02400          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02401             if (!peer) {
02402                if (option_verbose > 2)
02403                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02404                peer = o;
02405             }
02406          } else if (o->chan && (o->chan == winner)) {
02407 
02408             ast_copy_string(on, o->member->interface, sizeof(on));
02409             ast_copy_string(membername, o->member->membername, sizeof(membername));
02410 
02411             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02412                if (option_verbose > 2)
02413                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02414                numnochan++;
02415                do_hang(o);
02416                winner = NULL;
02417                continue;
02418             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02419                char tmpchan[256];
02420                char *stuff;
02421                char *tech;
02422 
02423                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02424                if ((stuff = strchr(tmpchan, '/'))) {
02425                   *stuff++ = '\0';
02426                   tech = tmpchan;
02427                } else {
02428                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02429                   stuff = tmpchan;
02430                   tech = "Local";
02431                }
02432                /* Before processing channel, go ahead and check for forwarding */
02433                if (option_verbose > 2)
02434                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02435                /* Setup parameters */
02436                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02437                if (!o->chan) {
02438                   ast_log(LOG_NOTICE,
02439                      "Forwarding failed to create channel to dial '%s/%s'\n",
02440                      tech, stuff);
02441                   o->stillgoing = 0;
02442                   numnochan++;
02443                } else {
02444                   ast_channel_inherit_variables(in, o->chan);
02445                   ast_channel_datastore_inherit(in, o->chan);
02446                   if (o->chan->cid.cid_num)
02447                      free(o->chan->cid.cid_num);
02448                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02449 
02450                   if (o->chan->cid.cid_name)
02451                      free(o->chan->cid.cid_name);
02452                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02453 
02454                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02455                   o->chan->cdrflags = in->cdrflags;
02456 
02457                   if (in->cid.cid_ani) {
02458                      if (o->chan->cid.cid_ani)
02459                         free(o->chan->cid.cid_ani);
02460                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02461                   }
02462                   if (o->chan->cid.cid_rdnis)
02463                      free(o->chan->cid.cid_rdnis);
02464                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02465                   if (ast_call(o->chan, stuff, 0)) {
02466                      ast_log(LOG_NOTICE, "Forwarding failed to dial '%s/%s'\n",
02467                         tech, stuff);
02468                      do_hang(o);
02469                      numnochan++;
02470                   }
02471                }
02472                /* Hangup the original channel now, in case we needed it */
02473                ast_hangup(winner);
02474                continue;
02475             }
02476             f = ast_read(winner);
02477             if (f) {
02478                if (f->frametype == AST_FRAME_CONTROL) {
02479                   switch (f->subclass) {
02480                   case AST_CONTROL_ANSWER:
02481                      /* This is our guy if someone answered. */
02482                      if (!peer) {
02483                         if (option_verbose > 2)
02484                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02485                         peer = o;
02486                      }
02487                      break;
02488                   case AST_CONTROL_BUSY:
02489                      if (option_verbose > 2)
02490                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02491                      if (in->cdr)
02492                         ast_cdr_busy(in->cdr);
02493                      do_hang(o);
02494                      endtime = (long)time(NULL);
02495                      endtime -= starttime;
02496                      rna(endtime * 1000, qe, on, membername, 0);
02497                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02498                         if (qe->parent->timeoutrestart)
02499                            *to = orig;
02500                         /* Have enough time for a queue member to answer? */
02501                         if (*to > 500) {
02502                            ring_one(qe, outgoing, &numbusies);
02503                            starttime = (long) time(NULL);
02504                         }
02505                      }
02506                      numbusies++;
02507                      break;
02508                   case AST_CONTROL_CONGESTION:
02509                      if (option_verbose > 2)
02510                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02511                      if (in->cdr)
02512                         ast_cdr_failed(in->cdr);
02513                      endtime = (long)time(NULL);
02514                      endtime -= starttime;
02515                      rna(endtime * 1000, qe, on, membername, 0);
02516                      do_hang(o);
02517                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02518                         if (qe->parent->timeoutrestart)
02519                            *to = orig;
02520                         if (*to > 500) {
02521                            ring_one(qe, outgoing, &numbusies);
02522                            starttime = (long) time(NULL);
02523                         }
02524                      }
02525                      numbusies++;
02526                      break;
02527                   case AST_CONTROL_RINGING:
02528                      if (option_verbose > 2)
02529                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02530 
02531                      /* Start ring indication when the channel is ringing, if specified */
02532                      if (qe->ring_when_ringing) {
02533                         ast_moh_stop(qe->chan);
02534                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
02535                      }
02536                      break;
02537                   case AST_CONTROL_OFFHOOK:
02538                      /* Ignore going off hook */
02539                      break;
02540                   default:
02541                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02542                   }
02543                }
02544                ast_frfree(f);
02545             } else { /* ast_read() returned NULL */
02546                endtime = (long) time(NULL) - starttime;
02547                rna(endtime * 1000, qe, on, membername, 1);
02548                do_hang(o);
02549                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02550                   if (qe->parent->timeoutrestart)
02551                      *to = orig;
02552                   if (*to > 500) {
02553                      ring_one(qe, outgoing, &numbusies);
02554                      starttime = (long) time(NULL);
02555                   }
02556                }
02557             }
02558          }
02559       }
02560 
02561       /* If we received an event from the caller, deal with it. */
02562       if (winner == in) {
02563          f = ast_read(in);
02564          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02565             /* Got hung up */
02566             *to = -1;
02567             if (f)
02568                ast_frfree(f);
02569             return NULL;
02570          }
02571          /* First check if DTMF digit is a valid exit */
02572          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02573             if (option_verbose > 3)
02574                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02575             *to = 0;
02576             *digit = f->subclass;
02577             ast_frfree(f);
02578             if (in->cdr && (in->_state != AST_STATE_UP)) {
02579                ast_cdr_noanswer(in->cdr);
02580             }
02581             return NULL;
02582          }
02583          /* Else check if DTMF should be interpreted as caller disconnect */
02584          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02585             if (option_verbose > 3)
02586                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02587             *to = 0;
02588             ast_frfree(f);
02589             if (in->cdr && (in->_state != AST_STATE_UP)) {
02590                ast_cdr_noanswer(in->cdr);
02591             }
02592             return NULL;
02593          }
02594          ast_frfree(f);
02595       }
02596       if (!*to) {
02597          for (o = start; o; o = o->call_next)
02598             rna(orig, qe, o->interface, o->member->membername, 1);
02599       }
02600    }
02601 
02602    if (in->cdr
02603        && in->_state != AST_STATE_UP
02604        && (!*to || ast_check_hangup(in))) {
02605      ast_cdr_noanswer(in->cdr);
02606    }
02607 
02608    return peer;
02609 }

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

Return values:
0 if the caller's turn has arrived
-1 if the caller should exit the queue.

Definition at line 2671 of file app_queue.c.

References call_queue::announcefrequency, ast_queue_log(), ast_waitfordigit(), queue_ent::chan, queue_ent::expire, get_member_status(), is_our_turn(), leave_queue(), call_queue::leavewhenempty, queue_ent::max_penalty, call_queue::name, queue_ent::opos, queue_ent::parent, call_queue::periodicannouncefrequency, queue_ent::pos, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, RECHECK, say_periodic_announcement(), say_position(), queue_ent::start, and valid_exit().

Referenced by queue_exec().

02672 {
02673    int res = 0;
02674 
02675    /* This is the holding pen for callers 2 through maxlen */
02676    for (;;) {
02677       enum queue_member_status stat;
02678 
02679       if (is_our_turn(qe))
02680          break;
02681 
02682       /* If we have timed out, break out */
02683       if (qe->expire && (time(NULL) >= qe->expire)) {
02684          *reason = QUEUE_TIMEOUT;
02685          break;
02686       }
02687 
02688       stat = get_member_status(qe->parent, qe->max_penalty);
02689 
02690       /* leave the queue if no agents, if enabled */
02691       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02692          *reason = QUEUE_LEAVEEMPTY;
02693          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02694          leave_queue(qe);
02695          break;
02696       }
02697 
02698       /* leave the queue if no reachable agents, if enabled */
02699       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02700          *reason = QUEUE_LEAVEUNAVAIL;
02701          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02702          leave_queue(qe);
02703          break;
02704       }
02705 
02706       /* Make a position announcement, if enabled */
02707       if (qe->parent->announcefrequency && !ringing &&
02708          (res = say_position(qe)))
02709          break;
02710 
02711       /* If we have timed out, break out */
02712       if (qe->expire && (time(NULL) >= qe->expire)) {
02713          *reason = QUEUE_TIMEOUT;
02714          break;
02715       }
02716 
02717       /* Make a periodic announcement, if enabled */
02718       if (qe->parent->periodicannouncefrequency && !ringing &&
02719          (res = say_periodic_announcement(qe)))
02720          break;
02721 
02722       /* If we have timed out, break out */
02723       if (qe->expire && (time(NULL) >= qe->expire)) {
02724          *reason = QUEUE_TIMEOUT;
02725          break;
02726       }
02727       
02728       /* Wait a second before checking again */
02729       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02730          if (res > 0 && !valid_exit(qe, res))
02731             res = 0;
02732          else
02733             break;
02734       }
02735       
02736       /* If we have timed out, break out */
02737       if (qe->expire && (time(NULL) >= qe->expire)) {
02738          *reason = QUEUE_TIMEOUT;
02739          break;
02740       }
02741    }
02742 
02743    return res;
02744 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .description = "True Call Queueing" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "361d7bb937402d51e4658efb5b4d76e4" , .load = load_module, .unload = unload_module, .reload = reload, } [static]

Definition at line 5960 of file app_queue.c.

char* app = "Queue" [static]

Definition at line 152 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 198 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 200 of file app_queue.c.

char* app_aqm_synopsis = "Dynamically adds queue members" [static]

Definition at line 199 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 233 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 235 of file app_queue.c.

char* app_pqm_synopsis = "Pauses a queue member" [static]

Definition at line 234 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 272 of file app_queue.c.

char* app_ql_descrip [static]
Initial value:
"   QueueLog(queuename|uniqueid|agent|event[|additionalinfo]):\n"
"Allows you to write your own events into the queue log\n"
"Example: QueueLog(101|${UNIQUEID}|${AGENT}|WENTONBREAK|600)\n"

Definition at line 274 of file app_queue.c.

char* app_ql_synopsis = "Writes to the queue_log" [static]

Definition at line 273 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 217 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 219 of file app_queue.c.

char* app_rqm_synopsis = "Dynamically removes queue members" [static]

Definition at line 218 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 256 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 258 of file app_queue.c.

char* app_upqm_synopsis = "Unpauses a queue member" [static]

Definition at line 257 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5960 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 294 of file app_queue.c.

Initial value:
 {
   { "add", "queue", "member", NULL },
   handle_queue_add_member, NULL,
   NULL, complete_queue_add_member }

Definition at line 5837 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 5847 of file app_queue.c.

Referenced by load_module(), and unload_module().

Initial value:
 {
   { "remove", "queue", "member", NULL },
   handle_queue_remove_member, NULL,
   NULL, complete_queue_remove_member }

Definition at line 5842 of file app_queue.c.

Initial value:
 {
   { "show", "queue", NULL },
   queue_show, NULL,
   NULL, complete_queue_show }

Definition at line 5832 of file app_queue.c.

Condition for the state change queue

Definition at line 757 of file app_queue.c.

char* descrip [static]

Definition at line 156 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

Referenced by device_state_thread(), load_module(), statechange_queue(), and unload_module().

struct statechange* first

Definition at line 759 of file app_queue.c.

Definition at line 314 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 759 of file app_queue.c.

Lock for the state change queue

Definition at line 755 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 297 of file app_queue.c.

const char* pm_family = "Queue/PersistentMembers" [static]

Persistent Members astdb family.

Definition at line 280 of file app_queue.c.

char qam_cmd_usage[] [static]
Initial value:
"Usage: queue add member <channel> to <queue> [penalty <penalty> [as <membername> [state_interface <state_interface>]]]\n"

Definition at line 5826 of file app_queue.c.

char qmc_cmd_usage[] [static]
Initial value:
"Usage: queue member count <queue>\n"
"       Provides member count information on a specified queue.\n"

Definition at line 5803 of file app_queue.c.

char qrm_cmd_usage[] [static]
Initial value:
"Usage: queue remove member <channel> from <queue>\n"

Definition at line 5829 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 285 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 288 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

char queue_show_usage[] [static]
Initial value:
"Usage: queue show\n"
"       Provides summary information on a specified queue.\n"

Definition at line 5822 of file app_queue.c.

Initial value:
 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}

a datastore used to help correctly log attended transfers of queue callers

Definition at line 2874 of file app_queue.c.

Referenced by attended_transfer_occurred(), queue_transfer_fixup(), setup_transfer_datastore(), and try_calling().

Definition at line 4844 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4836 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4854 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4878 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4896 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4887 of file app_queue.c.

Referenced by load_module(), and unload_module().

Definition at line 4869 of file app_queue.c.

Referenced by load_module(), and unload_module().

int shared_lastcall = 0 [static]

queues.conf [general] option

Definition at line 300 of file app_queue.c.

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

Definition at line 751 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

char* synopsis = "Queue a call for a call queue" [static]

Definition at line 154 of file app_queue.c.

char* text
pthread_t thread

The device state monitoring thread

Definition at line 753 of file app_queue.c.

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 291 of file app_queue.c.


Generated on 8 Mar 2019 for Asterisk - the Open Source PBX by  doxygen 1.6.1