Tue Apr 6 15:45:38 2010

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
}
enum  qmc_status {
  QMC_VALID = 0, QMC_PAUSED, QMC_ACTIVE, QMC_FREE,
  QMC_ALL
}
enum  queue_member_status { QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL }
enum  queue_result {
  QUEUE_UNKNOWN = 0, QUEUE_TIMEOUT = 1, QUEUE_JOINEMPTY = 2, QUEUE_LEAVEEMPTY = 3,
  QUEUE_JOINUNAVAIL = 4, QUEUE_LEAVEUNAVAIL = 5, QUEUE_FULL = 6
}

Functions

static int __queues_show (struct mansession *s, int manager, int fd, int argc, char **argv)
static void __reg_module (void)
static void __unreg_module (void)
static int add_to_interfaces (const char *interface)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
static struct call_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_queuemembercount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuememberlist (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_function_queuewaitingcount (struct ast_channel *chan, char *cmd, char *data, char *buf, size_t len)
static int queue_member_count (const char *qname, struct member_count *qmc)
static void queue_set_param (struct call_queue *q, const char *param, const char *val, int linenum, int failunknown)
 Configure a queue parameter.
static int queue_show (int fd, int argc, char **argv)
static void queue_transfer_destroy (void *data)
static void queue_transfer_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
 Log an attended transfer when a queue caller channel is masqueraded.
static void recalc_holdtime (struct queue_ent *qe, int newholdtime)
static void record_abandoned (struct queue_ent *qe)
static int reload (void)
static void reload_queue_members (void)
static int reload_queues (void)
static int remove_from_interfaces (const char *interface)
static int remove_from_queue (const char *queuename, const char *interface)
static void remove_queue (struct call_queue *q)
 removes a call_queue from the list of call_queues
static int ring_entry (struct queue_ent *qe, struct callattempt *tmp, int *busies)
 Part 2 of ring_one.
static int ring_one (struct queue_ent *qe, struct callattempt *outgoing, int *busies)
 Place a call to a queue member.
static void rna (int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
 RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.
static int rqm_exec (struct ast_channel *chan, void *data)
static void rr_dep_warning (void)
static void rt_handle_member_record (struct call_queue *q, char *interface, const char *membername, const char *penalty_str, const char *paused_str, const char *state_interface)
static int say_periodic_announcement (struct queue_ent *qe)
static int say_position (struct queue_ent *qe)
static int set_member_paused (const char *queuename, const char *interface, int paused)
static void set_queue_result (struct ast_channel *chan, enum queue_result res)
 sets the QUEUESTATUS channel variable
static struct ast_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 (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_infoast_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 {
      statechange *   first
      statechange *   last
   }   state_change_q
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static char qam_cmd_usage []
static char qmc_cmd_usage []
static char qrm_cmd_usage []
static int queue_debug = 0
 queues.conf [general] extra debug option
static int queue_persistent_members = 0
 queues.conf [general] option
struct {
   enum queue_result   id
   char *   text
queue_results []
static char queue_show_usage []
static struct ast_datastore_info queue_transfer_info
 a datastore used to help correctly log attended transfers of queue callers
static struct ast_custom_function queueagentcount_function
static struct ast_custom_function queuemembercount_function
static struct ast_custom_function queuememberlist_function
static struct ast_custom_function queuewaitingcount_function
static struct strategy strategies []
static char * synopsis = "Queue a call for a call queue"
static int use_weight = 0
 queues.conf per-queue weight option


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>:

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

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 393 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2226 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 137 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 138 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 140 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 277 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 390 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

Referenced by queue_set_param(), and ring_entry().

#define RECHECK   1

Definition at line 139 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Definition at line 143 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOSUCHQUEUE   (-3)

Definition at line 145 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_NOT_DYNAMIC   (-4)

Definition at line 146 of file app_queue.c.

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

#define RES_OKAY   0

Definition at line 142 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), remove_from_queue(), and rqm_exec().

#define RES_OUTOFMEMORY   (-2)

Definition at line 144 of file app_queue.c.

Referenced by add_to_queue(), aqm_exec(), handle_queue_add_member(), handle_queue_remove_member(), manager_add_queue_member(), manager_remove_queue_member(), and reload_queue_members().


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 

Definition at line 116 of file app_queue.c.

enum qmc_status

Enumerator:
QMC_VALID 
QMC_PAUSED 
QMC_ACTIVE 
QMC_FREE 
QMC_ALL 

Definition at line 4317 of file app_queue.c.

04317                 {
04318    QMC_VALID = 0, /* Count valid members */
04319    QMC_PAUSED,    /* Count paused members */
04320    QMC_ACTIVE,    /* Count active members */
04321    QMC_FREE,      /* Count free members */
04322    QMC_ALL        /* Count all queue members */
04323 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 570 of file app_queue.c.

00570                          {
00571    QUEUE_NO_MEMBERS,
00572    QUEUE_NO_REACHABLE_MEMBERS,
00573    QUEUE_NORMAL
00574 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 294 of file app_queue.c.

00294                   {
00295    QUEUE_UNKNOWN = 0,
00296    QUEUE_TIMEOUT = 1,
00297    QUEUE_JOINEMPTY = 2,
00298    QUEUE_LEAVEEMPTY = 3,
00299    QUEUE_JOINUNAVAIL = 4,
00300    QUEUE_LEAVEUNAVAIL = 5,
00301    QUEUE_FULL = 6,
00302 };


Function Documentation

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

Definition at line 4755 of file app_queue.c.

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

Referenced by manager_queues_show(), and queue_show().

04756 {
04757    struct call_queue *q;
04758    struct queue_ent *qe;
04759    struct member *mem;
04760    int pos, queue_show;
04761    time_t now;
04762    char max_buf[150];
04763    char *max;
04764    size_t max_left;
04765    float sl = 0;
04766    char *term = manager ? "\r\n" : "\n";
04767    struct ao2_iterator mem_iter;
04768 
04769    time(&now);
04770    if (argc == 2)
04771       queue_show = 0;
04772    else if (argc == 3)
04773       queue_show = 1;
04774    else
04775       return RESULT_SHOWUSAGE;
04776 
04777    /* We only want to load realtime queues when a specific queue is asked for. */
04778    if (queue_show) {
04779       load_realtime_queue(argv[2]);
04780    } else if (ast_check_realtime("queues")) {
04781       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
04782       char *queuename;
04783       if (cfg) {
04784          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
04785             load_realtime_queue(queuename);
04786          }
04787          ast_config_destroy(cfg);
04788       }
04789    }
04790 
04791    AST_LIST_LOCK(&queues);
04792    if (AST_LIST_EMPTY(&queues)) {
04793       AST_LIST_UNLOCK(&queues);
04794       if (queue_show) {
04795          if (s)
04796             astman_append(s, "No such queue: %s.%s",argv[2], term);
04797          else
04798             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04799       } else {
04800          if (s)
04801             astman_append(s, "No queues.%s", term);
04802          else
04803             ast_cli(fd, "No queues.%s", term);
04804       }
04805       return RESULT_SUCCESS;
04806    }
04807    AST_LIST_TRAVERSE(&queues, q, list) {
04808       ao2_lock(q);
04809       if (queue_show) {
04810          if (strcasecmp(q->name, argv[2]) != 0) {
04811             ao2_unlock(q);
04812             if (!AST_LIST_NEXT(q, list)) {
04813                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04814                break;
04815             }
04816             continue;
04817          }
04818       }
04819       max_buf[0] = '\0';
04820       max = max_buf;
04821       max_left = sizeof(max_buf);
04822       if (q->maxlen)
04823          ast_build_string(&max, &max_left, "%d", q->maxlen);
04824       else
04825          ast_build_string(&max, &max_left, "unlimited");
04826       sl = 0;
04827       if (q->callscompleted > 0)
04828          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04829       if (s)
04830          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",
04831                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04832                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04833       else
04834          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",
04835                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04836                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04837       if (ao2_container_count(q->members)) {
04838          if (s)
04839             astman_append(s, "   Members: %s", term);
04840          else
04841             ast_cli(fd, "   Members: %s", term);
04842          mem_iter = ao2_iterator_init(q->members, 0);
04843          while ((mem = ao2_iterator_next(&mem_iter))) {
04844             max_buf[0] = '\0';
04845             max = max_buf;
04846             max_left = sizeof(max_buf);
04847             if (strcasecmp(mem->membername, mem->interface)) {
04848                ast_build_string(&max, &max_left, " (%s)", mem->interface);
04849             }
04850             if (mem->penalty)
04851                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04852             if (mem->dynamic)
04853                ast_build_string(&max, &max_left, " (dynamic)");
04854             if (mem->realtime)
04855                ast_build_string(&max, &max_left, " (realtime)");
04856             if (mem->paused)
04857                ast_build_string(&max, &max_left, " (paused)");
04858             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04859             if (mem->calls) {
04860                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04861                   mem->calls, (long) (time(NULL) - mem->lastcall));
04862             } else
04863                ast_build_string(&max, &max_left, " has taken no calls yet");
04864             if (s)
04865                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04866             else
04867                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04868             ao2_ref(mem, -1);
04869          }
04870          ao2_iterator_destroy(&mem_iter);
04871       } else if (s)
04872          astman_append(s, "   No Members%s", term);
04873       else  
04874          ast_cli(fd, "   No Members%s", term);
04875       if (q->head) {
04876          pos = 1;
04877          if (s)
04878             astman_append(s, "   Callers: %s", term);
04879          else
04880             ast_cli(fd, "   Callers: %s", term);
04881          for (qe = q->head; qe; qe = qe->next) {
04882             if (s)
04883                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04884                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04885                   (long) (now - qe->start) % 60, qe->prio, term);
04886             else
04887                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04888                   qe->chan->name, (long) (now - qe->start) / 60,
04889                   (long) (now - qe->start) % 60, qe->prio, term);
04890          }
04891       } else if (s)
04892          astman_append(s, "   No Callers%s", term);
04893       else
04894          ast_cli(fd, "   No Callers%s", term);
04895       if (s)
04896          astman_append(s, "%s", term);
04897       else
04898          ast_cli(fd, "%s", term);
04899       ao2_unlock(q);
04900       if (queue_show)
04901          break;
04902    }
04903    AST_LIST_UNLOCK(&queues);
04904    return RESULT_SUCCESS;
04905 }

static void __reg_module ( void   )  [static]

Definition at line 5583 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5583 of file app_queue.c.

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

Definition at line 917 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, member_interface::list, LOG_DEBUG, and option_debug.

Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().

00918 {
00919    struct member_interface *curint;
00920 
00921    AST_LIST_LOCK(&interfaces);
00922    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00923       if (!strcasecmp(curint->interface, interface))
00924          break;
00925    }
00926 
00927    if (curint) {
00928       AST_LIST_UNLOCK(&interfaces);
00929       return 0;
00930    }
00931 
00932    if (option_debug)
00933       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00934    
00935    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00936       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00937       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00938    }
00939    AST_LIST_UNLOCK(&interfaces);
00940 
00941    return 0;
00942 }

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

03533 {
03534    struct call_queue *q;
03535    struct member *new_member, *old_member;
03536    int res = RES_NOSUCHQUEUE;
03537 
03538    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03539     * short-circuits if the queue is already in memory. */
03540    if (!(q = load_realtime_queue(queuename)))
03541       return res;
03542 
03543    AST_LIST_LOCK(&queues);
03544 
03545    ao2_lock(q);
03546    if ((old_member = interface_exists(q, interface)) == NULL) {
03547       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03548          add_to_interfaces(new_member->state_interface);
03549          new_member->dynamic = 1;
03550          ao2_link(q->members, new_member);
03551          q->membercount++;
03552          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03553             "Queue: %s\r\n"
03554             "Location: %s\r\n"
03555             "MemberName: %s\r\n"
03556             "Membership: %s\r\n"
03557             "Penalty: %d\r\n"
03558             "CallsTaken: %d\r\n"
03559             "LastCall: %d\r\n"
03560             "Status: %d\r\n"
03561             "Paused: %d\r\n",
03562             q->name, new_member->interface, new_member->membername,
03563             "dynamic",
03564             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03565             new_member->status, new_member->paused);
03566          
03567          ao2_ref(new_member, -1);
03568          new_member = NULL;
03569 
03570          if (dump)
03571             dump_queue_members(q);
03572          
03573          res = RES_OKAY;
03574       } else {
03575          res = RES_OUTOFMEMORY;
03576       }
03577    } else {
03578       ao2_ref(old_member, -1);
03579       res = RES_EXISTS;
03580    }
03581    ao2_unlock(q);
03582    AST_LIST_UNLOCK(&queues);
03583 
03584    return res;
03585 }

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

Definition at line 819 of file app_queue.c.

References ao2_alloc(), ast_copy_string(), and destroy_queue().

Referenced by find_queue_by_name_rt(), and reload_queues().

00820 {
00821    struct call_queue *q;
00822 
00823    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
00824       ast_copy_string(q->name, queuename, sizeof(q->name));
00825    }
00826    return q;
00827 }

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

Definition at line 3913 of file app_queue.c.

References add_to_queue(), AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, LOG_ERROR, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and ast_channel::uniqueid.

Referenced by load_module().

03914 {
03915    int res=-1;
03916    struct ast_module_user *lu;
03917    char *parse, *temppos = NULL;
03918    int priority_jump = 0;
03919    AST_DECLARE_APP_ARGS(args,
03920       AST_APP_ARG(queuename);
03921       AST_APP_ARG(interface);
03922       AST_APP_ARG(penalty);
03923       AST_APP_ARG(options);
03924       AST_APP_ARG(membername);
03925       AST_APP_ARG(state_interface);
03926    );
03927    int penalty = 0;
03928 
03929    if (ast_strlen_zero(data)) {
03930       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
03931       return -1;
03932    }
03933 
03934    parse = ast_strdupa(data);
03935 
03936    AST_STANDARD_APP_ARGS(args, parse);
03937 
03938    lu = ast_module_user_add(chan);
03939 
03940    if (ast_strlen_zero(args.interface)) {
03941       args.interface = ast_strdupa(chan->name);
03942       temppos = strrchr(args.interface, '-');
03943       if (temppos)
03944          *temppos = '\0';
03945    }
03946 
03947    if (!ast_strlen_zero(args.penalty)) {
03948       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
03949          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03950          penalty = 0;
03951       }
03952    }
03953    
03954    if (args.options) {
03955       if (strchr(args.options, 'j'))
03956          priority_jump = 1;
03957    }
03958 
03959    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
03960    case RES_OKAY:
03961       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03962       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03963       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03964       res = 0;
03965       break;
03966    case RES_EXISTS:
03967       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03968       if (priority_jump || ast_opt_priority_jumping)
03969          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03970       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03971       res = 0;
03972       break;
03973    case RES_NOSUCHQUEUE:
03974       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03975       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03976       res = 0;
03977       break;
03978    case RES_OUTOFMEMORY:
03979       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03980       break;
03981    }
03982 
03983    ast_module_user_remove(lu);
03984 
03985    return res;
03986 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

02772 {
02773    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02774 }

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

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

Referenced by try_calling().

02650 {
02651    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02652       return -1;
02653 
02654    switch (q->strategy) {
02655    case QUEUE_STRATEGY_RINGALL:
02656       /* Everyone equal, except for penalty */
02657       tmp->metric = mem->penalty * 1000000;
02658       break;
02659    case QUEUE_STRATEGY_ROUNDROBIN:
02660       if (!pos) {
02661          if (!q->wrapped) {
02662             /* No more channels, start over */
02663             q->rrpos = 0;
02664          } else {
02665             /* Prioritize next entry */
02666             q->rrpos++;
02667          }
02668          q->wrapped = 0;
02669       }
02670       /* Fall through */
02671    case QUEUE_STRATEGY_RRMEMORY:
02672       if (pos < q->rrpos) {
02673          tmp->metric = 1000 + pos;
02674       } else {
02675          if (pos > q->rrpos)
02676             /* Indicate there is another priority */
02677             q->wrapped = 1;
02678          tmp->metric = pos;
02679       }
02680       tmp->metric += mem->penalty * 1000000;
02681       break;
02682    case QUEUE_STRATEGY_RANDOM:
02683       tmp->metric = ast_random() % 1000;
02684       tmp->metric += mem->penalty * 1000000;
02685       break;
02686    case QUEUE_STRATEGY_FEWESTCALLS:
02687       tmp->metric = mem->calls;
02688       tmp->metric += mem->penalty * 1000000;
02689       break;
02690    case QUEUE_STRATEGY_LEASTRECENT:
02691       if (!mem->lastcall)
02692          tmp->metric = 0;
02693       else
02694          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02695       tmp->metric += mem->penalty * 1000000;
02696       break;
02697    default:
02698       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02699       break;
02700    }
02701    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02702       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02703    }
02704    if (option_debug)
02705       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02706                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02707    return 0;
02708 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 996 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, free, and member_interface::list.

Referenced by unload_module().

00997 {
00998    struct member_interface *curint;
00999 
01000    AST_LIST_LOCK(&interfaces);
01001    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01002       free(curint);
01003    AST_LIST_UNLOCK(&interfaces);
01004 }

static void clear_queue ( struct call_queue q  )  [static]

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

00909 {
00910    q->holdtime = 0;
00911    q->callscompleted = 0;
00912    q->callsabandoned = 0;
00913    q->callscompletedinsl = 0;
00914    q->wrapuptime = 0;
00915 }

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

Definition at line 5410 of file app_queue.c.

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

05411 {
05412    char buffer[256] = "";
05413    char *queuename;
05414    
05415    if (argc != 4) {
05416       return RESULT_SHOWUSAGE;
05417    }
05418    queuename = argv[3];
05419 
05420    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05421          ast_cli(fd, 
05422                      "Member count for queue '%s'\n"
05423                      "%s\n",
05424                      queuename, buffer); 
05425          return RESULT_SUCCESS;
05426    } else {
05427          ast_cli(fd, "No such queue: '%s'\n", queuename);
05428          return RESULT_FAILURE;
05429    }
05430 }

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

Definition at line 1828 of file app_queue.c.

References ao2_find(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_TRAVERSE, ast_log(), call_queue::count, member::interface, member_interface::list, LOG_DEBUG, call_queue::members, call_queue::name, num_available_members(), and call_queue::weight.

Referenced by ring_entry().

01829 {
01830    struct call_queue *q;
01831    struct member *mem;
01832    int found = 0;
01833    
01834    /* &qlock and &rq->lock already set by try_calling()
01835     * to solve deadlock */
01836    AST_LIST_TRAVERSE(&queues, q, list) {
01837       if (q == rq) /* don't check myself, could deadlock */
01838          continue;
01839       ao2_lock(q);
01840       if (q->count && q->members) {
01841          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01842             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01843             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
01844                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);
01845                found = 1;
01846             }
01847             ao2_ref(mem, -1);
01848          }
01849       }
01850       ao2_unlock(q);
01851       if (found)
01852          break;
01853    }
01854    return found;
01855 }

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

Definition at line 4912 of file app_queue.c.

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

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

04913 {
04914    struct call_queue *q;
04915    char *ret = NULL;
04916    int which = 0;
04917    int wordlen = strlen(word);
04918    
04919    AST_LIST_LOCK(&queues);
04920    AST_LIST_TRAVERSE(&queues, q, list) {
04921       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04922          ret = ast_strdup(q->name); 
04923          break;
04924       }
04925    }
04926    AST_LIST_UNLOCK(&queues);
04927 
04928    return ret;
04929 }

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

Definition at line 5215 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05216 {
05217    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05218    switch (pos) {
05219    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05220       return NULL;
05221    case 4:  /* only one possible match, "to" */
05222       return state == 0 ? ast_strdup("to") : NULL;
05223    case 5:  /* <queue> */
05224       return complete_queue(line, word, pos, state);
05225    case 6: /* only one possible match, "penalty" */
05226       return state == 0 ? ast_strdup("penalty") : NULL;
05227    case 7:
05228       if (state < 100) {   /* 0-99 */
05229          char *num;
05230          if ((num = ast_malloc(3))) {
05231             sprintf(num, "%d", state);
05232          }
05233          return num;
05234       } else {
05235          return NULL;
05236       }
05237    case 8: /* only one possible match, "as" */
05238       return state == 0 ? ast_strdup("as") : NULL;
05239    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05240       return NULL;
05241    case 10:
05242       return state == 0 ? ast_strdup("state_interface") : NULL;
05243    default:
05244       return NULL;
05245    }
05246 }

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

Definition at line 5437 of file app_queue.c.

References complete_queue().

05438 {
05439       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05440       switch (pos) {
05441       case 3:  /* <queue> */
05442             return complete_queue(line, word, pos, state);
05443       default:
05444          return NULL;
05445       }
05446 }

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

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

05284 {
05285    int which = 0;
05286    struct call_queue *q;
05287    struct member *m;
05288    struct ao2_iterator mem_iter;
05289 
05290    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05291    if (pos > 5 || pos < 3)
05292       return NULL;
05293    if (pos == 4)  /* only one possible match, 'from' */
05294       return state == 0 ? ast_strdup("from") : NULL;
05295 
05296    if (pos == 5)  /* No need to duplicate code */
05297       return complete_queue(line, word, pos, state);
05298 
05299    /* here is the case for 3, <member> */
05300    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05301       AST_LIST_TRAVERSE(&queues, q, list) {
05302          ao2_lock(q);
05303          mem_iter = ao2_iterator_init(q->members, 0);
05304          while ((m = ao2_iterator_next(&mem_iter))) {
05305             if (++which > state) {
05306                char *tmp;
05307                ao2_iterator_destroy(&mem_iter);
05308                ao2_unlock(q);
05309                tmp = ast_strdup(m->interface);
05310                ao2_ref(m, -1);
05311                return tmp;
05312             }
05313             ao2_ref(m, -1);
05314          }
05315          ao2_iterator_destroy(&mem_iter);
05316          ao2_unlock(q);
05317       }
05318    }
05319 
05320    return NULL;
05321 }

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

Definition at line 4931 of file app_queue.c.

References complete_queue().

04932 {
04933    if (pos == 2)
04934       return complete_queue(line, word, pos, state);
04935    return NULL;
04936 }

static int compress_char ( const char  c  )  [static]

Definition at line 829 of file app_queue.c.

00830 {
00831    if (c < 32)
00832       return 0;
00833    else if (c > 96)
00834       return c - 64;
00835    else
00836       return c - 32;
00837 }

static struct member* create_queue_member ( const char *  interface,
const char *  membername,
int  penalty,
int  paused,
const char *  state_interface 
) [static]

allocate space for new queue member and set fields based on parameters passed

Definition at line 794 of file app_queue.c.

References ao2_alloc(), ast_copy_string(), ast_device_state(), ast_log(), ast_strlen_zero(), LOG_WARNING, and member::penalty.

Referenced by add_to_queue(), reload_queues(), and rt_handle_member_record().

00795 {
00796    struct member *cur;
00797    
00798    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00799       cur->penalty = penalty;
00800       cur->paused = paused;
00801       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00802       if (!ast_strlen_zero(state_interface)) {
00803          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00804       } else {
00805          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00806       }
00807       if (!ast_strlen_zero(membername))
00808          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00809       else
00810          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00811       if (!strchr(cur->interface, '/'))
00812          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00813       cur->status = ast_device_state(cur->state_interface);
00814    }
00815 
00816    return cur;
00817 }

static void destroy_queue ( void *  obj  )  [static]

Definition at line 536 of file app_queue.c.

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

Referenced by alloc_queue().

00537 {
00538    struct call_queue *q = obj;
00539    if (q->members) {
00540       free_members(q, 1);
00541       ao2_ref(q->members, -1);
00542    }
00543 }

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

Consumer of the statechange queue.

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

00743 {
00744    struct statechange *sc = NULL;
00745 
00746    while (!device_state.stop) {
00747       ast_mutex_lock(&device_state.lock);
00748       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00749          ast_cond_wait(&device_state.cond, &device_state.lock);
00750          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00751       }
00752       ast_mutex_unlock(&device_state.lock);
00753 
00754       /* Check to see if we were woken up to see the request to stop */
00755       if (device_state.stop)
00756          break;
00757 
00758       if (!sc)
00759          continue;
00760 
00761       handle_statechange(sc);
00762 
00763       free(sc);
00764       sc = NULL;
00765    }
00766 
00767    if (sc)
00768       free(sc);
00769 
00770    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00771       free(sc);
00772 
00773    return NULL;
00774 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1858 of file app_queue.c.

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

Referenced by ring_entry().

01859 {
01860    o->stillgoing = 0;
01861    ast_hangup(o->chan);
01862    o->chan = NULL;
01863 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

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

03441 {
03442    struct member *cur_member;
03443    char value[PM_MAX_LEN];
03444    int value_len = 0;
03445    int res;
03446    struct ao2_iterator mem_iter;
03447 
03448    memset(value, 0, sizeof(value));
03449 
03450    if (!pm_queue)
03451       return;
03452 
03453    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03454    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03455       if (!cur_member->dynamic) {
03456          ao2_ref(cur_member, -1);
03457          continue;
03458       }
03459 
03460       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03461          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03462 
03463       ao2_ref(cur_member, -1);
03464 
03465       if (res != strlen(value + value_len)) {
03466          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03467          break;
03468       }
03469       value_len += res;
03470    }
03471    ao2_iterator_destroy(&mem_iter);
03472    
03473    if (value_len && !cur_member) {
03474       if (ast_db_put(pm_family, pm_queue->name, value))
03475          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03476    } else
03477       /* Delete the entry if the queue is empty or there is an error */
03478       ast_db_del(pm_family, pm_queue->name);
03479 }

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

find the entry with the best metric, or NULL

Definition at line 2065 of file app_queue.c.

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

02066 {
02067    struct callattempt *best = NULL, *cur;
02068 
02069    for (cur = outgoing; cur; cur = cur->q_next) {
02070       if (cur->stillgoing &&              /* Not already done */
02071          !cur->chan &&              /* Isn't already going */
02072          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02073          best = cur;
02074       }
02075    }
02076 
02077    return best;
02078 }

static struct call_queue* find_queue_by_name_rt ( const char *  queuename,
struct ast_variable queue_vars,
struct ast_config member_config 
) [static]

Reload a single queue via realtime.

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

References alloc_queue(), ao2_lock(), ao2_unlock(), ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_TRAVERSE, ast_log(), ast_strlen_zero(), clear_queue(), init_queue(), member_interface::interface, member_interface::list, LOG_DEBUG, LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), call_queue::realtime, remove_queue(), and ast_variable::value.

Referenced by load_realtime_queue().

01233 {
01234    struct ast_variable *v;
01235    struct call_queue *q;
01236    struct member *m;
01237    struct ao2_iterator mem_iter;
01238    char *interface = NULL;
01239    char *tmp, *tmp_name;
01240    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01241 
01242    /* Find the queue in the in-core list (we will create a new one if not found). */
01243    AST_LIST_TRAVERSE(&queues, q, list) {
01244       if (!strcasecmp(q->name, queuename))
01245          break;
01246    }
01247 
01248    /* Static queues override realtime. */
01249    if (q) {
01250       ao2_lock(q);
01251       if (!q->realtime) {
01252          if (q->dead) {
01253             ao2_unlock(q);
01254             return NULL;
01255          } else {
01256             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01257             ao2_unlock(q);
01258             return q;
01259          }
01260       }
01261    } else if (!member_config)
01262       /* Not found in the list, and it's not realtime ... */
01263       return NULL;
01264 
01265    /* Check if queue is defined in realtime. */
01266    if (!queue_vars) {
01267       /* Delete queue from in-core list if it has been deleted in realtime. */
01268       if (q) {
01269          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01270             found condition... So we might delete an in-core queue
01271             in case of DB failure. */
01272          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01273 
01274          q->dead = 1;
01275          /* Delete if unused (else will be deleted when last caller leaves). */
01276          if (!q->count) {
01277             /* Delete. */
01278             ao2_unlock(q);
01279             remove_queue(q);
01280          } else
01281             ao2_unlock(q);
01282       }
01283       return NULL;
01284    }
01285 
01286    /* Create a new queue if an in-core entry does not exist yet. */
01287    if (!q) {
01288       if (!(q = alloc_queue(queuename)))
01289          return NULL;
01290       ao2_lock(q);
01291       clear_queue(q);
01292       q->realtime = 1;
01293       AST_LIST_INSERT_HEAD(&queues, q, list);
01294    }
01295    init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01296 
01297    memset(tmpbuf, 0, sizeof(tmpbuf));
01298    for (v = queue_vars; v; v = v->next) {
01299       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01300       if ((tmp = strchr(v->name, '_'))) {
01301          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01302          tmp_name = tmpbuf;
01303          tmp = tmp_name;
01304          while ((tmp = strchr(tmp, '_')))
01305             *tmp++ = '-';
01306       } else
01307          tmp_name = v->name;
01308 
01309       if (!ast_strlen_zero(v->value)) {
01310          /* Don't want to try to set the option if the value is empty */
01311          queue_set_param(q, tmp_name, v->value, -1, 0);
01312       }
01313    }
01314 
01315    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01316       rr_dep_warning();
01317 
01318    /* Temporarily set realtime members dead so we can detect deleted ones. 
01319     * Also set the membercount correctly for realtime*/
01320    mem_iter = ao2_iterator_init(q->members, 0);
01321    while ((m = ao2_iterator_next(&mem_iter))) {
01322       q->membercount++;
01323       if (m->realtime)
01324          m->dead = 1;
01325       ao2_ref(m, -1);
01326    }
01327    ao2_iterator_destroy(&mem_iter);
01328 
01329    while ((interface = ast_category_browse(member_config, interface))) {
01330       rt_handle_member_record(q, interface,
01331          ast_variable_retrieve(member_config, interface, "membername"),
01332          ast_variable_retrieve(member_config, interface, "penalty"),
01333          ast_variable_retrieve(member_config, interface, "paused"),
01334          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"),interface));
01335    }
01336 
01337    /* Delete all realtime members that have been deleted in DB. */
01338    mem_iter = ao2_iterator_init(q->members, 0);
01339    while ((m = ao2_iterator_next(&mem_iter))) {
01340       if (m->dead) {
01341          ao2_unlink(q->members, m);
01342          ao2_unlock(q);
01343          remove_from_interfaces(m->state_interface);
01344          ao2_lock(q);
01345          q->membercount--;
01346       }
01347       ao2_ref(m, -1);
01348    }
01349    ao2_iterator_destroy(&mem_iter);
01350 
01351    ao2_unlock(q);
01352 
01353    return q;
01354 }

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

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

01213 {
01214    /* Free non-dynamic members */
01215    struct member *cur;
01216    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01217 
01218    while ((cur = ao2_iterator_next(&mem_iter))) {
01219       if (all || !cur->dynamic) {
01220          ao2_unlink(q->members, cur);
01221          remove_from_interfaces(cur->state_interface);
01222          q->membercount--;
01223       }
01224       ao2_ref(cur, -1);
01225    }
01226    ao2_iterator_destroy(&mem_iter);
01227 }

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

00583 {
00584    struct member *member;
00585    struct ao2_iterator mem_iter;
00586    enum queue_member_status result = QUEUE_NO_MEMBERS;
00587 
00588    ao2_lock(q);
00589    mem_iter = ao2_iterator_init(q->members, 0);
00590    while ((member = ao2_iterator_next(&mem_iter))) {
00591       if (max_penalty && (member->penalty > max_penalty)) {
00592          ao2_ref(member, -1);
00593          continue;
00594       }
00595 
00596       if (member->paused) {
00597          ao2_ref(member, -1);
00598          continue;
00599       }
00600 
00601       switch (member->status) {
00602       case AST_DEVICE_INVALID:
00603          /* nothing to do */
00604          ao2_ref(member, -1);
00605          break;
00606       case AST_DEVICE_UNAVAILABLE:
00607          result = QUEUE_NO_REACHABLE_MEMBERS;
00608          ao2_ref(member, -1);
00609          break;
00610       default:
00611          ao2_unlock(q);
00612          ao2_ref(member, -1);
00613          return QUEUE_NORMAL;
00614       }
00615    }
00616    ao2_iterator_destroy(&mem_iter);
00617    ao2_unlock(q);
00618    return result;
00619 }

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

Definition at line 5155 of file app_queue.c.

References add_to_queue(), ast_cli(), ast_queue_log(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

05156 {
05157    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05158    int penalty;
05159 
05160    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05161       return RESULT_SHOWUSAGE;
05162    } else if (strcmp(argv[4], "to")) {
05163       return RESULT_SHOWUSAGE;
05164    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05165       return RESULT_SHOWUSAGE;
05166    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05167       return RESULT_SHOWUSAGE;
05168    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05169       return RESULT_SHOWUSAGE;
05170    }
05171 
05172    queuename = argv[5];
05173    interface = argv[3];
05174    if (argc >= 8) {
05175       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05176          if (penalty < 0) {
05177             ast_cli(fd, "Penalty must be >= 0\n");
05178             penalty = 0;
05179          }
05180       } else {
05181          ast_cli(fd, "Penalty must be an integer >= 0\n");
05182          penalty = 0;
05183       }
05184    } else {
05185       penalty = 0;
05186    }
05187 
05188    if (argc >= 10) {
05189       membername = argv[9];
05190    }
05191 
05192    if (argc >= 12) {
05193       state_interface = argv[11];
05194    }
05195 
05196    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05197    case RES_OKAY:
05198       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05199       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05200       return RESULT_SUCCESS;
05201    case RES_EXISTS:
05202       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05203       return RESULT_FAILURE;
05204    case RES_NOSUCHQUEUE:
05205       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05206       return RESULT_FAILURE;
05207    case RES_OUTOFMEMORY:
05208       ast_cli(fd, "Out of memory\n");
05209       return RESULT_FAILURE;
05210    default:
05211       return RESULT_FAILURE;
05212    }
05213 }

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

Definition at line 5248 of file app_queue.c.

References ast_cli(), ast_queue_log(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, RESULT_FAILURE, RESULT_SHOWUSAGE, and RESULT_SUCCESS.

05249 {
05250    char *queuename, *interface;
05251 
05252    if (argc != 6) {
05253       return RESULT_SHOWUSAGE;
05254    } else if (strcmp(argv[4], "from")) {
05255       return RESULT_SHOWUSAGE;
05256    }
05257 
05258    queuename = argv[5];
05259    interface = argv[3];
05260 
05261    switch (remove_from_queue(queuename, interface)) {
05262    case RES_OKAY:
05263       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05264       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05265       return RESULT_SUCCESS;
05266    case RES_EXISTS:
05267       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05268       return RESULT_FAILURE;
05269    case RES_NOSUCHQUEUE:
05270       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05271       return RESULT_FAILURE;
05272    case RES_OUTOFMEMORY:
05273       ast_cli(fd, "Out of memory\n");
05274       return RESULT_FAILURE;
05275    case RES_NOT_DYNAMIC:
05276       ast_cli(fd, "Member not dynamic\n");
05277       return RESULT_FAILURE;
05278    default:
05279       return RESULT_FAILURE;
05280    }
05281 }

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 681 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, member_interface::list, LOG_DEBUG, option_debug, statechange::state, and update_status().

Referenced by device_state_thread().

00682 {
00683    struct member_interface *curint;
00684    char *loc;
00685    char *technology;
00686    char interface[80];
00687 
00688    technology = ast_strdupa(sc->dev);
00689    loc = strchr(technology, '/');
00690    if (loc) {
00691       *loc++ = '\0';
00692    } else {
00693       return NULL;
00694    }
00695 
00696    AST_LIST_LOCK(&interfaces);
00697    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00698       char *slash_pos;
00699       ast_copy_string(interface, curint->interface, sizeof(interface));
00700       if ((slash_pos = strchr(interface, '/')))
00701          if ((slash_pos = strchr(slash_pos + 1, '/')))
00702             *slash_pos = '\0';
00703 
00704       if (!strcasecmp(interface, sc->dev))
00705          break;
00706    }
00707    AST_LIST_UNLOCK(&interfaces);
00708 
00709    if (!curint) {
00710       if (option_debug > 2)
00711          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));
00712       return NULL;
00713    }
00714 
00715    if (option_debug)
00716       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00717 
00718    update_status(sc->dev, sc->state);
00719 
00720    return NULL;
00721 }

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

Definition at line 1759 of file app_queue.c.

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

01760 {
01761    struct callattempt *oo;
01762 
01763    while (outgoing) {
01764       /* Hangup any existing lines we have open */
01765       if (outgoing->chan && (outgoing->chan != exception))
01766          ast_hangup(outgoing->chan);
01767       oo = outgoing;
01768       outgoing = outgoing->q_next;
01769       if (oo->member)
01770          ao2_ref(oo->member, -1);
01771       free(oo);
01772    }
01773 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 857 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, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00858 {
00859    int i;
00860 
00861    q->dead = 0;
00862    q->retry = DEFAULT_RETRY;
00863    q->timeout = -1;
00864    q->maxlen = 0;
00865    q->ringlimit = 0;
00866    q->announcefrequency = 0;
00867    q->announceholdtime = 0;
00868    q->roundingseconds = 0; /* Default - don't announce seconds */
00869    q->servicelevel = 0;
00870    q->ringinuse = 1;
00871    q->setinterfacevar = 0;
00872    q->autofill = autofill_default;
00873    q->montype = montype_default;
00874    q->moh[0] = '\0';
00875    q->announce[0] = '\0';
00876    q->context[0] = '\0';
00877    q->monfmt[0] = '\0';
00878    q->periodicannouncefrequency = 0;
00879    q->reportholdtime = 0;
00880    q->monjoin = 0;
00881    q->wrapuptime = 0;
00882    q->joinempty = 0;
00883    q->leavewhenempty = 0;
00884    q->memberdelay = 0;
00885    q->maskmemberstatus = 0;
00886    q->eventwhencalled = 0;
00887    q->weight = 0;
00888    q->timeoutrestart = 0;
00889    if (!q->members)
00890       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00891    q->membercount = 0;
00892    q->found = 1;
00893    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00894    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00895    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00896    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00897    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00898    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00899    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00900    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00901    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00902    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00903    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00904       q->sound_periodicannounce[i][0]='\0';
00905    }
00906 }

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

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

Referenced by join_queue().

00547 {
00548    struct queue_ent *cur;
00549 
00550    if (!q || !new)
00551       return;
00552    if (prev) {
00553       cur = prev->next;
00554       prev->next = new;
00555    } else {
00556       cur = q->head;
00557       q->head = new;
00558    }
00559    new->next = cur;
00560 
00561    /* every queue_ent must have a reference to it's parent call_queue, this
00562     * reference does not go away until the end of the queue_ent's life, meaning
00563     * that even when the queue_ent leaves the call_queue this ref must remain. */
00564    ao2_ref(q, +1);
00565    new->parent = q;
00566    new->pos = ++(*pos);
00567    new->opos = *pos;
00568 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 500 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

00501 {
00502    int x;
00503 
00504    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00505       if (strategy == strategies[x].strategy)
00506          return strategies[x].name;
00507    }
00508 
00509    return "<unknown>";
00510 }

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

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

03414 {
03415    struct member *mem;
03416    struct ao2_iterator mem_iter;
03417 
03418    if (!q)
03419       return NULL;
03420 
03421    mem_iter = ao2_iterator_init(q->members, 0);
03422    while ((mem = ao2_iterator_next(&mem_iter))) {
03423       if (!strcasecmp(interface, mem->interface)) {
03424          ao2_iterator_destroy(&mem_iter);
03425          return mem;
03426       }
03427       ao2_ref(mem, -1);
03428    }
03429    ao2_iterator_destroy(&mem_iter);
03430 
03431    return NULL;
03432 }

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

Definition at line 944 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, member_interface::list, call_queue::members, and member::state_interface.

Referenced by remove_from_interfaces().

00945 {
00946    struct call_queue *q;
00947    struct member *mem;
00948    struct ao2_iterator mem_iter;
00949    int ret = 0;
00950 
00951    AST_LIST_LOCK(&queues);
00952    AST_LIST_TRAVERSE(&queues, q, list) {
00953       ao2_lock(q);
00954       mem_iter = ao2_iterator_init(q->members, 0);
00955       while ((mem = ao2_iterator_next(&mem_iter))) {
00956          if (!strcasecmp(mem->state_interface, interface)) {
00957             ao2_ref(mem, -1);
00958             ret = 1;
00959             break;
00960          }
00961          ao2_ref(mem, -1);
00962       }
00963       ao2_iterator_destroy(&mem_iter);
00964       ao2_unlock(q);
00965       if (ret)
00966          break;
00967    }
00968    AST_LIST_UNLOCK(&queues);
00969 
00970    return ret;
00971 }

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

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

Referenced by queue_exec(), and wait_our_turn().

02509 {
02510    struct queue_ent *ch;
02511    int res;
02512    int avl;
02513    int idx = 0;
02514    /* This needs a lock. How many members are available to be served? */
02515    ao2_lock(qe->parent);
02516 
02517    avl = num_available_members(qe->parent);
02518 
02519    ch = qe->parent->head;
02520 
02521    if (option_debug) {
02522       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02523    }
02524 
02525    while ((idx < avl) && (ch) && (ch != qe)) {
02526       if (!ch->pending)
02527          idx++;
02528       ch = ch->next;       
02529    }
02530 
02531    ao2_unlock(qe->parent);
02532 
02533    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02534    if (ch && idx < avl) {
02535       if (option_debug)
02536          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02537       res = 1;
02538    } else {
02539       if (option_debug)
02540          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02541       res = 0;
02542    }
02543 
02544    return res;
02545 }

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

Definition at line 1477 of file app_queue.c.

References 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, 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, queue_ent::moh, ast_channel::name, queue_ent::next, option_debug, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, S_OR, and ast_channel::uniqueid.

Referenced by queue_exec().

01478 {
01479    struct call_queue *q;
01480    struct queue_ent *cur, *prev = NULL;
01481    int res = -1;
01482    int pos = 0;
01483    int inserted = 0;
01484    enum queue_member_status stat;
01485 
01486    if (!(q = load_realtime_queue(queuename)))
01487       return res;
01488 
01489    AST_LIST_LOCK(&queues);
01490    ao2_lock(q);
01491 
01492    /* This is our one */
01493    stat = get_member_status(q, qe->max_penalty);
01494    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01495       *reason = QUEUE_JOINEMPTY;
01496    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
01497       *reason = QUEUE_JOINUNAVAIL;
01498    else if (q->maxlen && (q->count >= q->maxlen))
01499       *reason = QUEUE_FULL;
01500    else {
01501       /* There's space for us, put us at the right position inside
01502        * the queue.
01503        * Take into account the priority of the calling user */
01504       inserted = 0;
01505       prev = NULL;
01506       cur = q->head;
01507       while (cur) {
01508          /* We have higher priority than the current user, enter
01509           * before him, after all the other users with priority
01510           * higher or equal to our priority. */
01511          if ((!inserted) && (qe->prio > cur->prio)) {
01512             insert_entry(q, prev, qe, &pos);
01513             inserted = 1;
01514          }
01515          cur->pos = ++pos;
01516          prev = cur;
01517          cur = cur->next;
01518       }
01519       /* No luck, join at the end of the queue */
01520       if (!inserted)
01521          insert_entry(q, prev, qe, &pos);
01522       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01523       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01524       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01525       q->count++;
01526       res = 0;
01527       manager_event(EVENT_FLAG_CALL, "Join",
01528          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01529          qe->chan->name,
01530          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01531          S_OR(qe->chan->cid.cid_name, "unknown"),
01532          q->name, qe->pos, q->count, qe->chan->uniqueid );
01533       if (option_debug)
01534          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01535    }
01536    ao2_unlock(q);
01537    AST_LIST_UNLOCK(&queues);
01538 
01539    return res;
01540 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1718 of file app_queue.c.

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

Referenced by queue_exec(), and wait_our_turn().

01719 {
01720    struct call_queue *q;
01721    struct queue_ent *cur, *prev = NULL;
01722    int pos = 0;
01723 
01724    if (!(q = qe->parent))
01725       return;
01726    ao2_lock(q);
01727 
01728    prev = NULL;
01729    for (cur = q->head; cur; cur = cur->next) {
01730       if (cur == qe) {
01731          q->count--;
01732 
01733          /* Take us out of the queue */
01734          manager_event(EVENT_FLAG_CALL, "Leave",
01735             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01736             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01737          if (option_debug)
01738             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01739          /* Take us out of the queue */
01740          if (prev)
01741             prev->next = cur->next;
01742          else
01743             q->head = cur->next;
01744       } else {
01745          /* Renumber the people after us in the queue based on a new count */
01746          cur->pos = ++pos;
01747          prev = cur;
01748       }
01749    }
01750    ao2_unlock(q);
01751 
01752    if (q->dead && !q->count) {   
01753       /* It's dead and nobody is in it, so kill it */
01754       remove_queue(q);
01755    }
01756 }

static int load_module ( void   )  [static]

Definition at line 5537 of file app_queue.c.

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

05538 {
05539    int res;
05540 
05541    if (!reload_queues())
05542       return AST_MODULE_LOAD_DECLINE;
05543 
05544    if (queue_persistent_members)
05545       reload_queue_members();
05546 
05547    ast_mutex_init(&device_state.lock);
05548    ast_cond_init(&device_state.cond, NULL);
05549    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05550 
05551    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05552    res = ast_register_application(app, queue_exec, synopsis, descrip);
05553    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05554    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05555    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05556    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05557    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05558    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05559    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05560    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05561    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05562    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05563    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05564    res |= ast_custom_function_register(&queueagentcount_function);
05565    res |= ast_custom_function_register(&queuemembercount_function);
05566    res |= ast_custom_function_register(&queuememberlist_function);
05567    res |= ast_custom_function_register(&queuewaitingcount_function);
05568    res |= ast_devstate_add(statechange_queue, NULL);
05569 
05570    return res;
05571 }

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

Definition at line 1427 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(), member_interface::list, LOG_ERROR, call_queue::name, call_queue::realtime, and update_realtime_members().

Referenced by __queues_show(), add_to_queue(), join_queue(), queue_function_queuemembercount(), queue_member_count(), and reload_queue_members().

01428 {
01429    struct ast_variable *queue_vars;
01430    struct ast_config *member_config = NULL;
01431    struct call_queue *q;
01432 
01433    /* Find the queue in the in-core list first. */
01434    AST_LIST_LOCK(&queues);
01435    AST_LIST_TRAVERSE(&queues, q, list) {
01436       if (!strcasecmp(q->name, queuename)) {
01437          break;
01438       }
01439    }
01440    AST_LIST_UNLOCK(&queues);
01441 
01442    if (!q || q->realtime) {
01443       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01444          queue operations while waiting for the DB.
01445 
01446          This will be two separate database transactions, so we might
01447          see queue parameters as they were before another process
01448          changed the queue and member list as it was after the change.
01449          Thus we might see an empty member list when a queue is
01450          deleted. In practise, this is unlikely to cause a problem. */
01451 
01452       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01453       if (queue_vars) {
01454          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01455          if (!member_config) {
01456             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01457             ast_variables_destroy(queue_vars);
01458             return NULL;
01459          }
01460       }
01461 
01462       AST_LIST_LOCK(&queues);
01463 
01464       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01465       if (member_config)
01466          ast_config_destroy(member_config);
01467       if (queue_vars)
01468          ast_variables_destroy(queue_vars);
01469 
01470       AST_LIST_UNLOCK(&queues);
01471    } else { 
01472       update_realtime_members(q);
01473    }
01474    return q;
01475 }

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

Definition at line 5047 of file app_queue.c.

References add_to_queue(), ast_queue_log(), ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

05048 {
05049    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05050    int paused, penalty = 0;
05051 
05052    queuename = astman_get_header(m, "Queue");
05053    interface = astman_get_header(m, "Interface");
05054    penalty_s = astman_get_header(m, "Penalty");
05055    paused_s = astman_get_header(m, "Paused");
05056    membername = astman_get_header(m, "MemberName");
05057    state_interface = astman_get_header(m, "StateInterface");
05058 
05059    if (ast_strlen_zero(queuename)) {
05060       astman_send_error(s, m, "'Queue' not specified.");
05061       return 0;
05062    }
05063 
05064    if (ast_strlen_zero(interface)) {
05065       astman_send_error(s, m, "'Interface' not specified.");
05066       return 0;
05067    }
05068 
05069    if (ast_strlen_zero(penalty_s))
05070       penalty = 0;
05071    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05072       penalty = 0;
05073 
05074    if (ast_strlen_zero(paused_s))
05075       paused = 0;
05076    else
05077       paused = abs(ast_true(paused_s));
05078 
05079    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05080    case RES_OKAY:
05081       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05082       astman_send_ack(s, m, "Added interface to queue");
05083       break;
05084    case RES_EXISTS:
05085       astman_send_error(s, m, "Unable to add interface: Already there");
05086       break;
05087    case RES_NOSUCHQUEUE:
05088       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05089       break;
05090    case RES_OUTOFMEMORY:
05091       astman_send_error(s, m, "Out of memory");
05092       break;
05093    }
05094 
05095    return 0;
05096 }

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

Definition at line 5132 of file app_queue.c.

References ast_strlen_zero(), ast_true(), astman_get_header(), astman_send_ack(), astman_send_error(), s, and set_member_paused().

Referenced by load_module().

05133 {
05134    const char *queuename, *interface, *paused_s;
05135    int paused;
05136 
05137    interface = astman_get_header(m, "Interface");
05138    paused_s = astman_get_header(m, "Paused");
05139    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05140 
05141    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05142       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05143       return 0;
05144    }
05145 
05146    paused = abs(ast_true(paused_s));
05147 
05148    if (set_member_paused(queuename, interface, paused))
05149       astman_send_error(s, m, "Interface not found");
05150    else
05151       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05152    return 0;
05153 }

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

Definition at line 5392 of file app_queue.c.

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

Referenced by load_module().

05393 {
05394    char buffer[256] = "";
05395    const char *queuename = astman_get_header(m,"Queue");
05396 
05397    if (ast_strlen_zero(queuename)) {
05398          astman_send_error(s, m, "'Queue' not specified.");
05399          return 0;
05400    }
05401    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05402          astman_send_ack(s, m, buffer);
05403          return RESULT_SUCCESS;
05404    } else {
05405          astman_send_error(s, m, "Queue not found.");
05406          return 0;
05407    }
05408 }

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

Definition at line 4941 of file app_queue.c.

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

Referenced by load_module().

04942 {
04943    char *a[] = { "queue", "show" };
04944 
04945    __queues_show(s, 1, -1, 2, a);
04946    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04947 
04948    return RESULT_SUCCESS;
04949 }

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

Definition at line 4952 of file app_queue.c.

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

Referenced by load_module().

04953 {
04954    time_t now;
04955    int pos;
04956    const char *id = astman_get_header(m,"ActionID");
04957    const char *queuefilter = astman_get_header(m,"Queue");
04958    const char *memberfilter = astman_get_header(m,"Member");
04959    char idText[256] = "";
04960    struct call_queue *q;
04961    struct queue_ent *qe;
04962    float sl = 0;
04963    struct member *mem;
04964    struct ao2_iterator mem_iter;
04965 
04966    astman_send_ack(s, m, "Queue status will follow");
04967    time(&now);
04968    AST_LIST_LOCK(&queues);
04969    if (!ast_strlen_zero(id))
04970       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04971 
04972    AST_LIST_TRAVERSE(&queues, q, list) {
04973       ao2_lock(q);
04974 
04975       /* List queue properties */
04976       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04977          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04978          astman_append(s, "Event: QueueParams\r\n"
04979             "Queue: %s\r\n"
04980             "Max: %d\r\n"
04981             "Calls: %d\r\n"
04982             "Holdtime: %d\r\n"
04983             "Completed: %d\r\n"
04984             "Abandoned: %d\r\n"
04985             "ServiceLevel: %d\r\n"
04986             "ServicelevelPerf: %2.1f\r\n"
04987             "RingLimit: %d\r\n"
04988             "Weight: %d\r\n"
04989             "%s"
04990             "\r\n",
04991             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04992             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
04993          /* List Queue Members */
04994          mem_iter = ao2_iterator_init(q->members, 0);
04995          while ((mem = ao2_iterator_next(&mem_iter))) {
04996             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04997                astman_append(s, "Event: QueueMember\r\n"
04998                   "Queue: %s\r\n"
04999                   "Name: %s\r\n"
05000                   "Location: %s\r\n"
05001                   "Membership: %s\r\n"
05002                   "Penalty: %d\r\n"
05003                   "CallsTaken: %d\r\n"
05004                   "LastCall: %d\r\n"
05005                   "Status: %d\r\n"
05006                   "Paused: %d\r\n"
05007                   "%s"
05008                   "\r\n",
05009                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05010                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05011             }
05012             ao2_ref(mem, -1);
05013          }
05014          ao2_iterator_destroy(&mem_iter);
05015          /* List Queue Entries */
05016          pos = 1;
05017          for (qe = q->head; qe; qe = qe->next) {
05018             astman_append(s, "Event: QueueEntry\r\n"
05019                "Queue: %s\r\n"
05020                "Position: %d\r\n"
05021                "Channel: %s\r\n"
05022                "CallerID: %s\r\n"
05023                "CallerIDName: %s\r\n"
05024                "Wait: %ld\r\n"
05025                "%s"
05026                "\r\n",
05027                q->name, pos++, qe->chan->name,
05028                S_OR(qe->chan->cid.cid_num, "unknown"),
05029                S_OR(qe->chan->cid.cid_name, "unknown"),
05030                (long) (now - qe->start), idText);
05031          }
05032       }
05033       ao2_unlock(q);
05034    }
05035 
05036    astman_append(s,
05037       "Event: QueueStatusComplete\r\n"
05038       "%s"
05039       "\r\n",idText);
05040 
05041    AST_LIST_UNLOCK(&queues);
05042 
05043 
05044    return RESULT_SUCCESS;
05045 }

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

Definition at line 5098 of file app_queue.c.

References ast_queue_log(), ast_strlen_zero(), astman_get_header(), astman_send_ack(), astman_send_error(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, RES_OUTOFMEMORY, and s.

Referenced by load_module().

05099 {
05100    const char *queuename, *interface;
05101 
05102    queuename = astman_get_header(m, "Queue");
05103    interface = astman_get_header(m, "Interface");
05104 
05105    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05106       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05107       return 0;
05108    }
05109 
05110    switch (remove_from_queue(queuename, interface)) {
05111    case RES_OKAY:
05112       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05113       astman_send_ack(s, m, "Removed interface from queue");
05114       break;
05115    case RES_EXISTS:
05116       astman_send_error(s, m, "Unable to remove interface: Not there");
05117       break;
05118    case RES_NOSUCHQUEUE:
05119       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05120       break;
05121    case RES_OUTOFMEMORY:
05122       astman_send_error(s, m, "Out of memory");
05123       break;
05124    case RES_NOT_DYNAMIC:
05125       astman_send_error(s, m, "Member not dynamic");
05126       break;
05127    }
05128 
05129    return 0;
05130 }

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

Definition at line 851 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00852 {
00853    struct member *mem1 = obj1, *mem2 = obj2;
00854    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00855 }

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

Definition at line 839 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00840 {
00841    const struct member *mem = obj;
00842    const char *chname = strchr(mem->interface, '/');
00843    int ret = 0, i;
00844    if (!chname)
00845       chname = mem->interface;
00846    for (i = 0; i < 5 && chname[i]; i++)
00847       ret += compress_char(chname[i]) << (i * 6);
00848    return ret;
00849 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 479 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00480 {
00481    static unsigned int warned = 0;
00482    if (!warned) {
00483       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00484       warned = 1;
00485    }
00486 }

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 1783 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_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().

01784 {
01785    struct member *mem;
01786    int avl = 0;
01787    struct ao2_iterator mem_iter;
01788 
01789    mem_iter = ao2_iterator_init(q->members, 0);
01790    while ((mem = ao2_iterator_next(&mem_iter))) {
01791       switch (mem->status) {
01792       case AST_DEVICE_INUSE:
01793          if (!q->ringinuse)
01794             break;
01795          /* else fall through */
01796       case AST_DEVICE_NOT_INUSE:
01797       case AST_DEVICE_ONHOLD:
01798       case AST_DEVICE_RINGINUSE:
01799       case AST_DEVICE_UNKNOWN:
01800          if (!mem->paused) {
01801             avl++;
01802          }
01803          break;
01804       }
01805       ao2_ref(mem, -1);
01806 
01807       /* If autofill is not enabled or if the queue's strategy is ringall, then
01808        * we really don't care about the number of available members so much as we
01809        * do that there is at least one available.
01810        *
01811        * In fact, we purposely will return from this function stating that only
01812        * one member is available if either of those conditions hold. That way,
01813        * functions which determine what action to take based on the number of available
01814        * members will operate properly. The reasoning is that even if multiple
01815        * members are available, only the head caller can actually be serviced.
01816        */
01817       if ((!q->autofill || q->strategy == QUEUE_STRATEGY_RINGALL) && avl) {
01818          break;
01819       }
01820    }
01821    ao2_iterator_destroy(&mem_iter);
01822 
01823    return avl;
01824 }

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

Definition at line 1542 of file app_queue.c.

References AST_DIGIT_ANY, ast_stopstream(), ast_streamfile(), ast_strlen_zero(), ast_waitstream(), queue_ent::chan, and ast_channel::language.

Referenced by say_periodic_announcement(), and say_position().

01543 {
01544    int res;
01545 
01546    if (ast_strlen_zero(filename)) {
01547       return 0;
01548    }
01549 
01550    ast_stopstream(chan);
01551 
01552    res = ast_streamfile(chan, filename, chan->language);
01553    if (!res)
01554       res = ast_waitstream(chan, AST_DIGIT_ANY);
01555 
01556    ast_stopstream(chan);
01557 
01558    return res;
01559 }

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

Definition at line 3730 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().

Referenced by load_module().

03731 {
03732    struct ast_module_user *lu;
03733    char *parse;
03734    int priority_jump = 0;
03735    int ignore_fail = 0;
03736    AST_DECLARE_APP_ARGS(args,
03737       AST_APP_ARG(queuename);
03738       AST_APP_ARG(interface);
03739       AST_APP_ARG(options);
03740    );
03741 
03742    if (ast_strlen_zero(data)) {
03743       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03744       return -1;
03745    }
03746 
03747    parse = ast_strdupa(data);
03748 
03749    AST_STANDARD_APP_ARGS(args, parse);
03750 
03751    lu = ast_module_user_add(chan);
03752 
03753    if (args.options) {
03754       if (strchr(args.options, 'j'))
03755          priority_jump = 1;
03756       if (strchr(args.options, 'i'))
03757          ignore_fail = 1;
03758    }
03759 
03760    if (ast_strlen_zero(args.interface)) {
03761       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03762       ast_module_user_remove(lu);
03763       return -1;
03764    }
03765 
03766    if (set_member_paused(args.queuename, args.interface, 1)) {
03767       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03768       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03769       if (priority_jump || ast_opt_priority_jumping) {
03770          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03771             ast_module_user_remove(lu);
03772             return 0;
03773          }
03774       }
03775       ast_module_user_remove(lu);
03776       if (ignore_fail) {
03777          return 0;
03778       } else {
03779          return -1;
03780       }
03781    }
03782 
03783    ast_module_user_remove(lu);
03784    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03785    return 0;
03786 }

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

Definition at line 3988 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_log(), ast_module_user_add, ast_module_user_remove, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, LOG_WARNING, and parse().

Referenced by load_module().

03989 {
03990    struct ast_module_user *u;
03991    char *parse;
03992 
03993    AST_DECLARE_APP_ARGS(args,
03994       AST_APP_ARG(queuename);
03995       AST_APP_ARG(uniqueid);
03996       AST_APP_ARG(membername);
03997       AST_APP_ARG(event);
03998       AST_APP_ARG(params);
03999    );
04000 
04001    if (ast_strlen_zero(data)) {
04002       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04003       return -1;
04004    }
04005 
04006    u = ast_module_user_add(chan);
04007 
04008    parse = ast_strdupa(data);
04009 
04010    AST_STANDARD_APP_ARGS(args, parse);
04011 
04012    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04013        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04014       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04015       ast_module_user_remove(u);
04016       return -1;
04017    }
04018 
04019    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04020       "%s", args.params ? args.params : "");
04021 
04022    ast_module_user_remove(u);
04023 
04024    return 0;
04025 }

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

Definition at line 5377 of file app_queue.c.

References queue_member_count(), RESULT_FAILURE, and RESULT_SUCCESS.

Referenced by cli_queue_member_count(), and manager_queue_member_count().

05378 {
05379       struct member_count qmc;
05380       memset(&qmc, 0, sizeof(qmc));
05381       if (queue_member_count(queuename, &qmc) != 0) {
05382             return RESULT_FAILURE; 
05383       } else {
05384             snprintf(buffer, len, 
05385                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05386                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05387             return RESULT_SUCCESS;
05388       }
05389 }

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

The starting point for all queue calls.

The process involved here is to 1. Parse the options specified in the call to Queue() 2. Join the queue 3. Wait in a loop until it is our turn to try calling a queue member 4. Attempt to call a queue member 5. If 4. did not result in a bridged call, then check for between call options such as periodic announcements etc. 6. Try 4 again uless some condition (such as an expiration time) causes us to exit the queue.

Definition at line 4039 of file app_queue.c.

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

Referenced by load_module().

04040 {
04041    int res=-1;
04042    int ringing=0;
04043    struct ast_module_user *lu;
04044    const char *user_priority;
04045    const char *max_penalty_str;
04046    int prio;
04047    int max_penalty;
04048    enum queue_result reason = QUEUE_UNKNOWN;
04049    /* whether to exit Queue application after the timeout hits */
04050    int tries = 0;
04051    int noption = 0;
04052    char *parse;
04053    AST_DECLARE_APP_ARGS(args,
04054       AST_APP_ARG(queuename);
04055       AST_APP_ARG(options);
04056       AST_APP_ARG(url);
04057       AST_APP_ARG(announceoverride);
04058       AST_APP_ARG(queuetimeoutstr);
04059       AST_APP_ARG(agi);
04060    );
04061    /* Our queue entry */
04062    struct queue_ent qe = { 0 };
04063    
04064    if (ast_strlen_zero(data)) {
04065       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04066       return -1;
04067    }
04068    
04069    parse = ast_strdupa(data);
04070    AST_STANDARD_APP_ARGS(args, parse);
04071 
04072    lu = ast_module_user_add(chan);
04073 
04074    /* Setup our queue entry */
04075    qe.start = time(NULL);
04076 
04077    /* set the expire time based on the supplied timeout; */
04078    if (!ast_strlen_zero(args.queuetimeoutstr))
04079       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04080    else
04081       qe.expire = 0;
04082 
04083    /* Get the priority from the variable ${QUEUE_PRIO} */
04084    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04085    if (user_priority) {
04086       if (sscanf(user_priority, "%30d", &prio) == 1) {
04087          if (option_debug)
04088             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04089                chan->name, prio);
04090       } else {
04091          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04092             user_priority, chan->name);
04093          prio = 0;
04094       }
04095    } else {
04096       if (option_debug > 2)
04097          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04098       prio = 0;
04099    }
04100 
04101    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04102    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04103       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04104          if (option_debug)
04105             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04106                chan->name, max_penalty);
04107       } else {
04108          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04109             max_penalty_str, chan->name);
04110          max_penalty = 0;
04111       }
04112    } else {
04113       max_penalty = 0;
04114    }
04115 
04116    if (args.options && (strchr(args.options, 'r')))
04117       ringing = 1;
04118 
04119    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
04120       qe.ring_when_ringing = 1;
04121    }
04122 
04123    if (option_debug)
04124       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04125          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04126 
04127    qe.chan = chan;
04128    qe.prio = prio;
04129    qe.max_penalty = max_penalty;
04130    qe.last_pos_said = 0;
04131    qe.last_pos = 0;
04132    qe.last_periodic_announce_time = time(NULL);
04133    qe.last_periodic_announce_sound = 0;
04134    qe.valid_digits = 0;
04135    if (!join_queue(args.queuename, &qe, &reason)) {
04136       int makeannouncement = 0;
04137 
04138       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04139          S_OR(chan->cid.cid_num, ""));
04140 check_turns:
04141       if (ringing) {
04142          ast_indicate(chan, AST_CONTROL_RINGING);
04143       } else {
04144          ast_moh_start(chan, qe.moh, NULL);
04145       }
04146 
04147       /* This is the wait loop for callers 2 through maxlen */
04148       res = wait_our_turn(&qe, ringing, &reason);
04149       if (res)
04150          goto stop;
04151 
04152       for (;;) {
04153          /* This is the wait loop for the head caller*/
04154          /* To exit, they may get their call answered; */
04155          /* they may dial a digit from the queue context; */
04156          /* or, they may timeout. */
04157 
04158          enum queue_member_status stat;
04159 
04160          /* Leave if we have exceeded our queuetimeout */
04161          if (qe.expire && (time(NULL) >= qe.expire)) {
04162             record_abandoned(&qe);
04163             ast_cdr_noanswer(qe.chan->cdr);
04164             reason = QUEUE_TIMEOUT;
04165             res = 0;
04166             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04167             break;
04168          }
04169 
04170          if (makeannouncement) {
04171             /* Make a position announcement, if enabled */
04172             if (qe.parent->announcefrequency && !ringing)
04173                if ((res = say_position(&qe)))
04174                   goto stop;
04175 
04176          }
04177          makeannouncement = 1;
04178 
04179          /* Leave if we have exceeded our queuetimeout */
04180          if (qe.expire && (time(NULL) >= qe.expire)) {
04181             record_abandoned(&qe);
04182             ast_cdr_noanswer(qe.chan->cdr);
04183             reason = QUEUE_TIMEOUT;
04184             res = 0;
04185             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04186             break;
04187          }
04188          /* Make a periodic announcement, if enabled */
04189          if (qe.parent->periodicannouncefrequency && !ringing)
04190             if ((res = say_periodic_announcement(&qe)))
04191                goto stop;
04192 
04193          /* Leave if we have exceeded our queuetimeout */
04194          if (qe.expire && (time(NULL) >= qe.expire)) {
04195             record_abandoned(&qe);
04196             ast_cdr_noanswer(qe.chan->cdr);
04197             reason = QUEUE_TIMEOUT;
04198             res = 0;
04199             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04200             break;
04201          }
04202          /* Try calling all queue members for 'timeout' seconds */
04203          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04204          if (res)
04205             goto stop;
04206 
04207          stat = get_member_status(qe.parent, qe.max_penalty);
04208 
04209          /* exit after 'timeout' cycle if 'n' option enabled */
04210          if (noption && tries >= qe.parent->membercount) {
04211             if (option_verbose > 2)
04212                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04213             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04214             record_abandoned(&qe);
04215             ast_cdr_noanswer(qe.chan->cdr);
04216             reason = QUEUE_TIMEOUT;
04217             res = 0;
04218             break;
04219          }
04220 
04221          /* leave the queue if no agents, if enabled */
04222          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04223             record_abandoned(&qe);
04224             ast_cdr_noanswer(qe.chan->cdr);
04225             reason = QUEUE_LEAVEEMPTY;
04226             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04227             res = 0;
04228             break;
04229          }
04230 
04231          /* leave the queue if no reachable agents, if enabled */
04232          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04233             record_abandoned(&qe);
04234             ast_cdr_noanswer(qe.chan->cdr);
04235             reason = QUEUE_LEAVEUNAVAIL;
04236             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04237             res = 0;
04238             break;
04239          }
04240 
04241          /* Leave if we have exceeded our queuetimeout */
04242          if (qe.expire && (time(NULL) >= qe.expire)) {
04243             record_abandoned(&qe);
04244             ast_cdr_noanswer(qe.chan->cdr);
04245             reason = QUEUE_TIMEOUT;
04246             res = 0;
04247             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04248             break;
04249          }
04250 
04251          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04252          update_realtime_members(qe.parent);
04253 
04254          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04255          res = wait_a_bit(&qe);
04256          if (res)
04257             goto stop;
04258 
04259          /* Since this is a priority queue and
04260           * it is not sure that we are still at the head
04261           * of the queue, go and check for our turn again.
04262           */
04263          if (!is_our_turn(&qe)) {
04264             if (option_debug)
04265                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04266                   qe.chan->name);
04267             goto check_turns;
04268          }
04269       }
04270 
04271 stop:
04272       if (res) {
04273          if (res < 0) {
04274             if (!qe.handled) {
04275                record_abandoned(&qe);
04276                ast_cdr_noanswer(qe.chan->cdr);
04277                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04278                   "%d|%d|%ld", qe.pos, qe.opos,
04279                   (long) time(NULL) - qe.start);
04280             }
04281             res = -1;
04282          } else if (qe.valid_digits) {
04283             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04284                "%s|%d", qe.digits, qe.pos);
04285          }
04286       }
04287 
04288       /* Don't allow return code > 0 */
04289       if (res >= 0) {
04290          res = 0; 
04291          if (ringing) {
04292             ast_indicate(chan, -1);
04293          } else {
04294             ast_moh_stop(chan);
04295          }        
04296          ast_stopstream(chan);
04297       }
04298       leave_queue(&qe);
04299       if (reason != QUEUE_UNKNOWN)
04300          set_queue_result(chan, reason);
04301    } else {
04302       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04303       set_queue_result(chan, reason);
04304       res = 0;
04305    }
04306    if (qe.parent) {
04307       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
04308        * This ref must be taken away right before the queue_ent is destroyed.  In this case
04309        * the queue_ent is about to be returned on the stack */
04310       ao2_ref(qe.parent, -1);
04311    }
04312    ast_module_user_remove(lu);
04313 
04314    return res;
04315 }

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

Definition at line 4325 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, name, member::paused, QMC_ACTIVE, QMC_FREE, QMC_PAUSED, QMC_VALID, and member::status.

04326 {
04327    int count = 0;
04328    struct call_queue *q;
04329    struct ast_module_user *lu;
04330    struct member *m;
04331    struct ao2_iterator mem_iter;
04332    char *name, *item;
04333    enum qmc_status mode = QMC_VALID;
04334 
04335    buf[0] = '\0';
04336    
04337    if (ast_strlen_zero(data)) {
04338       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04339       return -1;
04340    }
04341 
04342    name = ast_strdupa(data);
04343 
04344    lu = ast_module_user_add(chan);
04345 
04346    if ((item = strchr(name, ':'))) {
04347       *item = '\0';
04348       item++;
04349    } else {
04350       item = "";
04351    }
04352 
04353    if (!strcasecmp(item, "valid")) {
04354       mode = QMC_VALID;
04355    } else  if (!strcasecmp(item, "paused")) {
04356       mode = QMC_PAUSED;
04357    } else  if (!strcasecmp(item, "active")) {
04358       mode = QMC_ACTIVE;
04359    } else  if (!strcasecmp(item, "free")) {
04360       mode = QMC_FREE;
04361    } else  if (!strcasecmp(item, "all")) {
04362       mode = QMC_ALL;
04363    }
04364 
04365    if ((q = load_realtime_queue(name))) {
04366       ao2_lock(q);
04367       mem_iter = ao2_iterator_init(q->members, 0);
04368       while ((m = ao2_iterator_next(&mem_iter))) {
04369          switch (mode) {
04370          case QMC_VALID:
04371             /* Count the queue members who are logged in and presently answering calls */
04372             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04373                count++;
04374             }
04375             break;
04376          case QMC_PAUSED:
04377             /* Count paused members */
04378             if (m->paused) {
04379                count++;
04380             }
04381             break;
04382          case QMC_ACTIVE:
04383             /* Count not paused members who are logged in and presently answering calls */
04384             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04385                count++;
04386             }
04387             break;
04388          case QMC_FREE:
04389             /* Count free members in the queue */
04390             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04391                count++;
04392             }
04393             break;
04394          default:
04395             count++;
04396             break;
04397          }
04398          ao2_ref(m, -1);
04399       }
04400       ao2_iterator_destroy(&mem_iter);
04401       ao2_unlock(q);
04402    } else
04403       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04404 
04405    snprintf(buf, len, "%d", count);
04406    ast_module_user_remove(lu);
04407 
04408    return 0;
04409 }

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

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

04455 {
04456    struct ast_module_user *u;
04457    struct call_queue *q;
04458    struct member *m;
04459 
04460    /* Ensure an otherwise empty list doesn't return garbage */
04461    buf[0] = '\0';
04462 
04463    if (ast_strlen_zero(data)) {
04464       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04465       return -1;
04466    }
04467    
04468    u = ast_module_user_add(chan);
04469 
04470    AST_LIST_LOCK(&queues);
04471    AST_LIST_TRAVERSE(&queues, q, list) {
04472       if (!strcasecmp(q->name, data)) {
04473          ao2_lock(q);
04474          break;
04475       }
04476    }
04477    AST_LIST_UNLOCK(&queues);
04478 
04479    if (q) {
04480       int buflen = 0, count = 0;
04481       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04482 
04483       while ((m = ao2_iterator_next(&mem_iter))) {
04484          /* strcat() is always faster than printf() */
04485          if (count++) {
04486             strncat(buf + buflen, ",", len - buflen - 1);
04487             buflen++;
04488          }
04489          strncat(buf + buflen, m->interface, len - buflen - 1);
04490          buflen += strlen(m->interface);
04491          /* Safeguard against overflow (negative length) */
04492          if (buflen >= len - 2) {
04493             ao2_ref(m, -1);
04494             ast_log(LOG_WARNING, "Truncating list\n");
04495             break;
04496          }
04497          ao2_ref(m, -1);
04498       }
04499       ao2_iterator_destroy(&mem_iter);
04500       ao2_unlock(q);
04501    } else
04502       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04503 
04504    /* We should already be terminated, but let's make sure. */
04505    buf[len - 1] = '\0';
04506    ast_module_user_remove(u);
04507 
04508    return 0;
04509 }

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

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

04412 {
04413    int count = 0;
04414    struct call_queue *q;
04415    struct ast_module_user *lu;
04416    struct ast_variable *var = NULL;
04417 
04418    buf[0] = '\0';
04419    
04420    if (ast_strlen_zero(data)) {
04421       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04422       return -1;
04423    }
04424 
04425    lu = ast_module_user_add(chan);
04426    
04427    AST_LIST_LOCK(&queues);
04428    AST_LIST_TRAVERSE(&queues, q, list) {
04429       if (!strcasecmp(q->name, data)) {
04430          ao2_lock(q);
04431          break;
04432       }
04433    }
04434    AST_LIST_UNLOCK(&queues);
04435 
04436    if (q) {
04437       count = q->count;
04438       ao2_unlock(q);
04439    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04440       /* if the queue is realtime but was not found in memory, this
04441        * means that the queue had been deleted from memory since it was 
04442        * "dead." This means it has a 0 waiting count
04443        */
04444       count = 0;
04445       ast_variables_destroy(var);
04446    } else
04447       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04448 
04449    snprintf(buf, len, "%d", count);
04450    ast_module_user_remove(lu);
04451    return 0;
04452 }

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

Definition at line 5335 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, member_count::paused, member::paused, member::status, and member_count::valid.

Referenced by qmc_handler().

05336 {
05337       int res = 0;
05338       struct call_queue *q;
05339       struct member *m;
05340       struct ao2_iterator mem_iter;
05341       
05342       if ((q = load_realtime_queue(qname))) {
05343             ao2_lock(q);
05344             mem_iter = ao2_iterator_init(q->members, 0);
05345             while ((m = ao2_iterator_next(&mem_iter))) {
05346                   /* Count the queue members in use */
05347                   if (m->status == AST_DEVICE_INUSE) {
05348                         qmc->inuse++;
05349                   }
05350                   /* Count the queue members who are logged in and presently answering calls */
05351                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05352                         qmc->valid++;
05353                   }
05354                   /* Count paused members */
05355                   if (m->paused) {
05356                         qmc->paused++;
05357                   }
05358                   /* Count not paused members who are logged in and presently answering calls */
05359                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05360                         qmc->active++;
05361                   }
05362                   /* Count free members in the queue */
05363                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05364                         qmc->free++;
05365                   }
05366                   qmc->all++;
05367                   ao2_ref(m, -1);
05368             }
05369             ao2_unlock(q);
05370       } else {
05371             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05372             res = -1;
05373       }
05374       return res;
05375 }

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

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

Referenced by find_queue_by_name_rt(), and reload_queues().

01014 {
01015    if (!strcasecmp(param, "musicclass") || 
01016       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
01017       ast_copy_string(q->moh, val, sizeof(q->moh));
01018    } else if (!strcasecmp(param, "announce")) {
01019       ast_copy_string(q->announce, val, sizeof(q->announce));
01020    } else if (!strcasecmp(param, "context")) {
01021       ast_copy_string(q->context, val, sizeof(q->context));
01022    } else if (!strcasecmp(param, "timeout")) {
01023       q->timeout = atoi(val);
01024       if (q->timeout < 0)
01025          q->timeout = DEFAULT_TIMEOUT;
01026    } else if (!strcasecmp(param, "ringinuse")) {
01027       q->ringinuse = ast_true(val);
01028    } else if (!strcasecmp(param, "setinterfacevar")) {
01029       q->setinterfacevar = ast_true(val);
01030    } else if (!strcasecmp(param, "monitor-join")) {
01031       monjoin_dep_warning();
01032       q->monjoin = ast_true(val);
01033    } else if (!strcasecmp(param, "monitor-format")) {
01034       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
01035    } else if (!strcasecmp(param, "queue-youarenext")) {
01036       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
01037    } else if (!strcasecmp(param, "queue-thereare")) {
01038       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
01039    } else if (!strcasecmp(param, "queue-callswaiting")) {
01040       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
01041    } else if (!strcasecmp(param, "queue-holdtime")) {
01042       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
01043    } else if (!strcasecmp(param, "queue-minutes")) {
01044       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
01045    } else if (!strcasecmp(param, "queue-seconds")) {
01046       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
01047    } else if (!strcasecmp(param, "queue-lessthan")) {
01048       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
01049    } else if (!strcasecmp(param, "queue-thankyou")) {
01050       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
01051    } else if (!strcasecmp(param, "queue-reporthold")) {
01052       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
01053    } else if (!strcasecmp(param, "announce-frequency")) {
01054       q->announcefrequency = atoi(val);
01055    } else if (!strcasecmp(param, "announce-round-seconds")) {
01056       q->roundingseconds = atoi(val);
01057       if (q->roundingseconds>60 || q->roundingseconds<0) {
01058          if (linenum >= 0) {
01059             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01060                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01061                val, param, q->name, linenum);
01062          } else {
01063             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01064                "using 0 instead for queue '%s'\n", val, param, q->name);
01065          }
01066          q->roundingseconds=0;
01067       }
01068    } else if (!strcasecmp(param, "announce-holdtime")) {
01069       if (!strcasecmp(val, "once"))
01070          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01071       else if (ast_true(val))
01072          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01073       else
01074          q->announceholdtime = 0;
01075    } else if (!strcasecmp(param, "periodic-announce")) {
01076       if (strchr(val, '|')) {
01077          char *s, *buf = ast_strdupa(val);
01078          unsigned int i = 0;
01079 
01080          while ((s = strsep(&buf, "|"))) {
01081             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
01082             i++;
01083             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01084                break;
01085          }
01086       } else {
01087          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
01088       }
01089    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01090       q->periodicannouncefrequency = atoi(val);
01091    } else if (!strcasecmp(param, "retry")) {
01092       q->retry = atoi(val);
01093       if (q->retry <= 0)
01094          q->retry = DEFAULT_RETRY;
01095    } else if (!strcasecmp(param, "wrapuptime")) {
01096       q->wrapuptime = atoi(val);
01097    } else if (!strcasecmp(param, "autofill")) {
01098       q->autofill = ast_true(val);
01099    } else if (!strcasecmp(param, "monitor-type")) {
01100       if (!strcasecmp(val, "mixmonitor"))
01101          q->montype = 1;
01102    } else if (!strcasecmp(param, "autopause")) {
01103       q->autopause = ast_true(val);
01104    } else if (!strcasecmp(param, "maxlen")) {
01105       q->maxlen = atoi(val);
01106       if (q->maxlen < 0)
01107          q->maxlen = 0;
01108    } else if (!strcasecmp(param, "ringlimit")) {
01109       q->ringlimit = atoi(val);
01110       if (q->ringlimit < 0)
01111          q->ringlimit = 0;
01112    } else if (!strcasecmp(param, "servicelevel")) {
01113       q->servicelevel= atoi(val);
01114    } else if (!strcasecmp(param, "strategy")) {
01115       q->strategy = strat2int(val);
01116       if (q->strategy < 0) {
01117          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01118             val, q->name);
01119          q->strategy = QUEUE_STRATEGY_RINGALL;
01120       }
01121    } else if (!strcasecmp(param, "joinempty")) {
01122       if (!strcasecmp(val, "strict"))
01123          q->joinempty = QUEUE_EMPTY_STRICT;
01124       else if (ast_true(val))
01125          q->joinempty = QUEUE_EMPTY_NORMAL;
01126       else
01127          q->joinempty = 0;
01128    } else if (!strcasecmp(param, "leavewhenempty")) {
01129       if (!strcasecmp(val, "strict"))
01130          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01131       else if (ast_true(val))
01132          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01133       else
01134          q->leavewhenempty = 0;
01135    } else if (!strcasecmp(param, "eventmemberstatus")) {
01136       q->maskmemberstatus = !ast_true(val);
01137    } else if (!strcasecmp(param, "eventwhencalled")) {
01138       if (!strcasecmp(val, "vars")) {
01139          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01140       } else {
01141          q->eventwhencalled = ast_true(val) ? 1 : 0;
01142       }
01143    } else if (!strcasecmp(param, "reportholdtime")) {
01144       q->reportholdtime = ast_true(val);
01145    } else if (!strcasecmp(param, "memberdelay")) {
01146       q->memberdelay = atoi(val);
01147    } else if (!strcasecmp(param, "weight")) {
01148       q->weight = atoi(val);
01149       if (q->weight)
01150          use_weight++;
01151       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01152          we will not see any effect on use_weight until next reload. */
01153    } else if (!strcasecmp(param, "timeoutrestart")) {
01154       q->timeoutrestart = ast_true(val);
01155    } else if (failunknown) {
01156       if (linenum >= 0) {
01157          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01158             q->name, param, linenum);
01159       } else {
01160          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01161       }
01162    }
01163 }

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

Definition at line 4907 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04908 {
04909    return __queues_show(NULL, 0, fd, argc, argv);
04910 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2717 of file app_queue.c.

References ast_free.

02718 {
02719    struct queue_transfer_ds *qtds = data;
02720    ast_free(qtds);
02721 }

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

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

02741 {
02742    struct queue_transfer_ds *qtds = data;
02743    struct queue_ent *qe = qtds->qe;
02744    struct member *member = qtds->member;
02745    time_t callstart = qtds->starttime;
02746    int callcompletedinsl = qtds->callcompletedinsl;
02747    struct ast_datastore *datastore;
02748 
02749    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02750             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02751             (long) (time(NULL) - callstart));
02752 
02753    update_queue(qe->parent, member, callcompletedinsl);
02754    
02755    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02756    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02757       ast_channel_datastore_remove(old_chan, datastore);
02758    } else {
02759       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02760    }
02761 }

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

Definition at line 1703 of file app_queue.c.

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

01704 {
01705    int oldvalue;
01706 
01707    /* Calculate holdtime using an exponential average */
01708    /* Thanks to SRT for this contribution */
01709    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01710 
01711    ao2_lock(qe->parent);
01712    oldvalue = qe->parent->holdtime;
01713    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01714    ao2_unlock(qe->parent);
01715 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2186 of file app_queue.c.

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

Referenced by queue_exec().

02187 {
02188    ao2_lock(qe->parent);
02189    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02190       "Queue: %s\r\n"
02191       "Uniqueid: %s\r\n"
02192       "Position: %d\r\n"
02193       "OriginalPosition: %d\r\n"
02194       "HoldTime: %d\r\n",
02195       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02196 
02197    qe->parent->callsabandoned++;
02198    ao2_unlock(qe->parent);
02199 }

static int reload ( void   )  [static]

Definition at line 5573 of file app_queue.c.

References reload_queues().

05574 {
05575    reload_queues();
05576    return 0;
05577 }

static void reload_queue_members ( void   )  [static]

Definition at line 3633 of file app_queue.c.

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

Referenced by load_module().

03634 {
03635    char *cur_ptr;
03636    char *queue_name;
03637    char *member;
03638    char *interface;
03639    char *membername = NULL;
03640    char *state_interface;
03641    char *penalty_tok;
03642    int penalty = 0;
03643    char *paused_tok;
03644    int paused = 0;
03645    struct ast_db_entry *db_tree;
03646    struct ast_db_entry *entry;
03647    struct call_queue *cur_queue;
03648    char queue_data[PM_MAX_LEN];
03649 
03650    AST_LIST_LOCK(&queues);
03651 
03652    /* Each key in 'pm_family' is the name of a queue */
03653    db_tree = ast_db_gettree(pm_family, NULL);
03654    for (entry = db_tree; entry; entry = entry->next) {
03655 
03656       queue_name = entry->key + strlen(pm_family) + 2;
03657 
03658       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03659          ao2_lock(cur_queue);
03660          if (!strcmp(queue_name, cur_queue->name))
03661             break;
03662          ao2_unlock(cur_queue);
03663       }
03664       
03665       if (!cur_queue)
03666          cur_queue = load_realtime_queue(queue_name);
03667 
03668       if (!cur_queue) {
03669          /* If the queue no longer exists, remove it from the
03670           * database */
03671          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03672          ast_db_del(pm_family, queue_name);
03673          continue;
03674       } else
03675          ao2_unlock(cur_queue);
03676 
03677       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03678          continue;
03679 
03680       cur_ptr = queue_data;
03681       while ((member = strsep(&cur_ptr, "|"))) {
03682          if (ast_strlen_zero(member))
03683             continue;
03684 
03685          interface = strsep(&member, ";");
03686          penalty_tok = strsep(&member, ";");
03687          paused_tok = strsep(&member, ";");
03688          membername = strsep(&member, ";");
03689          state_interface = strsep(&member,";");
03690 
03691          if (!penalty_tok) {
03692             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03693             break;
03694          }
03695          penalty = strtol(penalty_tok, NULL, 10);
03696          if (errno == ERANGE) {
03697             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03698             break;
03699          }
03700          
03701          if (!paused_tok) {
03702             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03703             break;
03704          }
03705          paused = strtol(paused_tok, NULL, 10);
03706          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03707             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03708             break;
03709          }
03710          if (ast_strlen_zero(membername))
03711             membername = interface;
03712 
03713          if (option_debug)
03714             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03715          
03716          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03717             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03718             break;
03719          }
03720       }
03721    }
03722 
03723    AST_LIST_UNLOCK(&queues);
03724    if (db_tree) {
03725       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03726       ast_db_freetree(db_tree);
03727    }
03728 }

static int reload_queues ( void   )  [static]

Definition at line 4554 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_load(), ast_copy_string(), AST_DECLARE_APP_ARGS, ast_free, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, 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(), LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::state_interface, call_queue::strategy, and var.

Referenced by load_module(), and reload().

04555 {
04556    struct call_queue *q;
04557    struct ast_config *cfg;
04558    char *cat, *tmp;
04559    struct ast_variable *var;
04560    struct member *cur, *newm;
04561    struct ao2_iterator mem_iter;
04562    int new;
04563    const char *general_val = NULL;
04564    char *parse;
04565    char *interface, *state_interface;
04566    char *membername = NULL;
04567    int penalty;
04568    AST_DECLARE_APP_ARGS(args,
04569       AST_APP_ARG(interface);
04570       AST_APP_ARG(penalty);
04571       AST_APP_ARG(membername);
04572       AST_APP_ARG(state_interface);
04573    );
04574    
04575    if (!(cfg = ast_config_load("queues.conf"))) {
04576       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04577       return 0;
04578    }
04579    AST_LIST_LOCK(&queues);
04580    use_weight=0;
04581    /* Mark all non-realtime queues as dead for the moment */
04582    AST_LIST_TRAVERSE(&queues, q, list) {
04583       if (!q->realtime) {
04584          q->dead = 1;
04585          q->found = 0;
04586       }
04587    }
04588 
04589    /* Chug through config file */
04590    cat = NULL;
04591    while ((cat = ast_category_browse(cfg, cat)) ) {
04592       if (!strcasecmp(cat, "general")) {  
04593          /* Initialize global settings */
04594          queue_debug = 0;
04595          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04596             queue_debug = ast_true(general_val);
04597          queue_persistent_members = 0;
04598          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04599             queue_persistent_members = ast_true(general_val);
04600          autofill_default = 0;
04601          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04602             autofill_default = ast_true(general_val);
04603          montype_default = 0;
04604          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04605             if (!strcasecmp(general_val, "mixmonitor"))
04606                montype_default = 1;
04607       } else { /* Define queue */
04608          /* Look for an existing one */
04609          AST_LIST_TRAVERSE(&queues, q, list) {
04610             if (!strcmp(q->name, cat))
04611                break;
04612          }
04613          if (!q) {
04614             /* Make one then */
04615             if (!(q = alloc_queue(cat))) {
04616                /* TODO: Handle memory allocation failure */
04617             }
04618             new = 1;
04619          } else
04620             new = 0;
04621          if (q) {
04622             if (!new)
04623                ao2_lock(q);
04624             /* Check if a queue with this name already exists */
04625             if (q->found) {
04626                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04627                if (!new)
04628                   ao2_unlock(q);
04629                continue;
04630             }
04631             /* Re-initialize the queue, and clear statistics */
04632             init_queue(q);
04633             clear_queue(q);
04634             mem_iter = ao2_iterator_init(q->members, 0);
04635             while ((cur = ao2_iterator_next(&mem_iter))) {
04636                if (!cur->dynamic) {
04637                   cur->delme = 1;
04638                }
04639                ao2_ref(cur, -1);
04640             }
04641             ao2_iterator_destroy(&mem_iter);
04642             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04643                if (!strcasecmp(var->name, "member")) {
04644                   struct member tmpmem;
04645                   membername = NULL;
04646 
04647                   if (ast_strlen_zero(var->value)) {
04648                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
04649                      continue;
04650                   }
04651 
04652                   /* Add a new member */
04653                   if (!(parse = ast_strdup(var->value))) {
04654                      continue;
04655                   }
04656                   
04657                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04658 
04659                   interface = args.interface;
04660                   if (!ast_strlen_zero(args.penalty)) {
04661                      tmp = ast_skip_blanks(args.penalty);
04662                      penalty = atoi(tmp);
04663                      if (penalty < 0) {
04664                         penalty = 0;
04665                      }
04666                   } else
04667                      penalty = 0;
04668 
04669                   if (!ast_strlen_zero(args.membername)) {
04670                      membername = ast_skip_blanks(args.membername);
04671                   }
04672 
04673                   if (!ast_strlen_zero(args.state_interface)) {
04674                      state_interface = ast_skip_blanks(args.state_interface);
04675                   } else {
04676                      state_interface = interface;
04677                   }
04678 
04679                   /* Find the old position in the list */
04680                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04681                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04682 
04683                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
04684                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
04685                      remove_from_interfaces(cur->state_interface);
04686                   }
04687 
04688                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
04689                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
04690                      add_to_interfaces(state_interface);
04691                   }
04692                   ao2_link(q->members, newm);
04693                   ao2_ref(newm, -1);
04694                   newm = NULL;
04695 
04696                   if (cur)
04697                      ao2_ref(cur, -1);
04698                   else {
04699                      q->membercount++;
04700                   }
04701                   ast_free(parse);
04702                } else {
04703                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04704                }
04705             }
04706 
04707             /* Free remaining members marked as delme */
04708             mem_iter = ao2_iterator_init(q->members, 0);
04709             while ((cur = ao2_iterator_next(&mem_iter))) {
04710                if (! cur->delme) {
04711                   ao2_ref(cur, -1);
04712                   continue;
04713                }
04714 
04715                q->membercount--;
04716                ao2_unlink(q->members, cur);
04717                remove_from_interfaces(cur->state_interface);
04718                ao2_ref(cur, -1);
04719             }
04720             ao2_iterator_destroy(&mem_iter);
04721 
04722             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04723                rr_dep_warning();
04724 
04725             if (new) {
04726                AST_LIST_INSERT_HEAD(&queues, q, list);
04727             } else
04728                ao2_unlock(q);
04729          }
04730       }
04731    }
04732    ast_config_destroy(cfg);
04733    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04734       if (q->dead) {
04735          AST_LIST_REMOVE_CURRENT(&queues, list);
04736          ao2_ref(q, -1);
04737       } else {
04738          ao2_lock(q);
04739          mem_iter = ao2_iterator_init(q->members, 0);
04740          while ((cur = ao2_iterator_next(&mem_iter))) {
04741             if (cur->dynamic)
04742                q->membercount++;
04743             cur->status = ast_device_state(cur->state_interface);
04744             ao2_ref(cur, -1);
04745          }
04746          ao2_iterator_destroy(&mem_iter);
04747          ao2_unlock(q);
04748       }
04749    }
04750    AST_LIST_TRAVERSE_SAFE_END;
04751    AST_LIST_UNLOCK(&queues);
04752    return 1;
04753 }

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

Definition at line 973 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(), member_interface::list, LOG_DEBUG, and option_debug.

Referenced by free_members(), reload_queues(), remove_from_queue(), rt_handle_member_record(), and update_realtime_members().

00974 {
00975    struct member_interface *curint;
00976 
00977    if (interface_exists_global(interface))
00978       return 0;
00979 
00980    AST_LIST_LOCK(&interfaces);
00981    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00982       if (!strcasecmp(curint->interface, interface)) {
00983          if (option_debug)
00984             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00985          AST_LIST_REMOVE_CURRENT(&interfaces, list);
00986          free(curint);
00987          break;
00988       }
00989    }
00990    AST_LIST_TRAVERSE_SAFE_END;
00991    AST_LIST_UNLOCK(&interfaces);
00992 
00993    return 0;
00994 }

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

Definition at line 3481 of file app_queue.c.

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

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

03482 {
03483    struct call_queue *q;
03484    struct member *mem, tmpmem;
03485    int res = RES_NOSUCHQUEUE;
03486 
03487    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03488 
03489    AST_LIST_LOCK(&queues);
03490    AST_LIST_TRAVERSE(&queues, q, list) {
03491       ao2_lock(q);
03492       if (strcmp(q->name, queuename)) {
03493          ao2_unlock(q);
03494          continue;
03495       }
03496 
03497       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03498          /* XXX future changes should beware of this assumption!! */
03499          if (!mem->dynamic) {
03500             res = RES_NOT_DYNAMIC;
03501             ao2_ref(mem, -1);
03502             ao2_unlock(q);
03503             break;
03504          }
03505          q->membercount--;
03506          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03507             "Queue: %s\r\n"
03508             "Location: %s\r\n"
03509             "MemberName: %s\r\n",
03510             q->name, mem->interface, mem->membername);
03511          ao2_unlink(q->members, mem);
03512          remove_from_interfaces(mem->state_interface);
03513          ao2_ref(mem, -1);
03514 
03515          if (queue_persistent_members)
03516             dump_queue_members(q);
03517          
03518          res = RES_OKAY;
03519       } else {
03520          res = RES_EXISTS;
03521       }
03522       ao2_unlock(q);
03523       break;
03524    }
03525 
03526    AST_LIST_UNLOCK(&queues);
03527 
03528    return res;
03529 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

Definition at line 527 of file app_queue.c.

References ao2_ref(), AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, and member_interface::list.

Referenced by find_queue_by_name_rt(), and leave_queue().

00528 {
00529    AST_LIST_LOCK(&queues);
00530    if (AST_LIST_REMOVE(&queues, q, list)) {
00531       ao2_ref(q, -1);
00532    }
00533    AST_LIST_UNLOCK(&queues);
00534 }

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 1905 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_state(), AST_DEVICE_UNKNOWN, ast_log(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_cdr::channel, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, ast_cdr::clid, compare_weight(), ast_channel::context, ast_channel::data, ast_cdr::dcontext, ast_channel::dialcontext, do_hang(), ast_cdr::dst, EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, callattempt::interface, ast_cdr::lastapp, callattempt::lastcall, ast_cdr::lastdata, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, ast_channel::name, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, 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().

01906 {
01907    int res;
01908    int status;
01909    char tech[256];
01910    char *location;
01911    const char *macrocontext, *macroexten;
01912 
01913    /* on entry here, we know that tmp->chan == NULL */
01914    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01915       if (queue_debug)
01916          ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface);
01917       if (qe->chan->cdr)
01918          ast_cdr_busy(qe->chan->cdr);
01919       tmp->stillgoing = 0;
01920       (*busies)++;
01921       return 0;
01922    }
01923 
01924    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01925       if (queue_debug)
01926          ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01927       if (qe->chan->cdr)
01928          ast_cdr_busy(qe->chan->cdr);
01929       tmp->stillgoing = 0;
01930       return 0;
01931    }
01932 
01933    if (tmp->member->paused) {
01934       if (queue_debug)
01935          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01936       if (qe->chan->cdr)
01937          ast_cdr_busy(qe->chan->cdr);
01938       tmp->stillgoing = 0;
01939       return 0;
01940    }
01941    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01942       if (queue_debug)
01943          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01944       if (qe->chan->cdr)
01945          ast_cdr_busy(qe->chan->cdr);
01946       tmp->stillgoing = 0;
01947       (*busies)++;
01948       return 0;
01949    }
01950 
01951    ast_copy_string(tech, tmp->interface, sizeof(tech));
01952    if ((location = strchr(tech, '/')))
01953       *location++ = '\0';
01954    else
01955       location = "";
01956 
01957    /* Request the peer */
01958    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01959    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01960       if (queue_debug)
01961          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
01962       if (qe->chan->cdr)
01963          ast_cdr_busy(qe->chan->cdr);
01964       tmp->stillgoing = 0;
01965 
01966       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
01967 
01968       ao2_lock(qe->parent);
01969       qe->parent->rrpos++;
01970       ao2_unlock(qe->parent);
01971 
01972       (*busies)++;
01973       return 0;
01974    }
01975    
01976    /* Increment ring count */
01977    tmp->member->ringcount++;
01978    tmp->chan->appl = "AppQueue";
01979    tmp->chan->data = "(Outgoing Line)";
01980    tmp->chan->whentohangup = 0;
01981    if (tmp->chan->cid.cid_num)
01982       free(tmp->chan->cid.cid_num);
01983    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01984    if (tmp->chan->cid.cid_name)
01985       free(tmp->chan->cid.cid_name);
01986    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01987    if (tmp->chan->cid.cid_ani)
01988       free(tmp->chan->cid.cid_ani);
01989    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01990 
01991    /* Inherit specially named variables from parent channel */
01992    ast_channel_inherit_variables(qe->chan, tmp->chan);
01993    ast_channel_datastore_inherit(qe->chan, tmp->chan);
01994 
01995    /* Presense of ADSI CPE on outgoing channel follows ours */
01996    tmp->chan->adsicpe = qe->chan->adsicpe;
01997 
01998    /* Inherit context and extension */
01999    ast_channel_lock(qe->chan);
02000    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
02001    if (!ast_strlen_zero(macrocontext))
02002       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
02003    else
02004       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
02005    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
02006    if (!ast_strlen_zero(macroexten))
02007       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
02008    else
02009       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
02010    if (ast_cdr_isset_unanswered()) {
02011       /* they want to see the unanswered dial attempts! */
02012       /* set up the CDR fields on all the CDRs to give sensical information */
02013       ast_cdr_setdestchan(tmp->chan->cdr, tmp->chan->name);
02014       strcpy(tmp->chan->cdr->clid, qe->chan->cdr->clid);
02015       strcpy(tmp->chan->cdr->channel, qe->chan->cdr->channel);
02016       strcpy(tmp->chan->cdr->src, qe->chan->cdr->src);
02017       strcpy(tmp->chan->cdr->dst, qe->chan->exten);
02018       strcpy(tmp->chan->cdr->dcontext, qe->chan->context);
02019       strcpy(tmp->chan->cdr->lastapp, qe->chan->cdr->lastapp);
02020       strcpy(tmp->chan->cdr->lastdata, qe->chan->cdr->lastdata);
02021       tmp->chan->cdr->amaflags = qe->chan->cdr->amaflags;
02022       strcpy(tmp->chan->cdr->accountcode, qe->chan->cdr->accountcode);
02023       strcpy(tmp->chan->cdr->userfield, qe->chan->cdr->userfield);
02024    }
02025    ast_channel_unlock(qe->chan);
02026 
02027    /* Place the call, but don't wait on the answer */
02028    if ((res = ast_call(tmp->chan, location, 0))) {
02029       /* Again, keep going even if there's an error */
02030       if (option_debug)
02031          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
02032       if (option_verbose > 2)
02033          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
02034       do_hang(tmp);
02035       (*busies)++;
02036       update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02037       return 0;
02038    } else if (qe->parent->eventwhencalled) {
02039       char vars[2048];
02040 
02041       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
02042                "AgentCalled: %s\r\n"
02043                "AgentName: %s\r\n"
02044                "ChannelCalling: %s\r\n"
02045                "CallerID: %s\r\n"
02046                "CallerIDName: %s\r\n"
02047                "Context: %s\r\n"
02048                "Extension: %s\r\n"
02049                "Priority: %d\r\n"
02050                "%s",
02051                tmp->interface, tmp->member->membername, qe->chan->name,
02052                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
02053                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
02054                qe->chan->context, qe->chan->exten, qe->chan->priority,
02055                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02056       if (option_verbose > 2)
02057          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
02058    }
02059 
02060    update_status(tmp->member->state_interface, ast_device_state(tmp->member->state_interface));
02061    return 1;
02062 }

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

02089 {
02090    int ret = 0;
02091 
02092    while (ret == 0) {
02093       struct callattempt *best = find_best(outgoing);
02094       if (!best) {
02095          if (option_debug)
02096             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02097          break;
02098       }
02099       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02100          struct callattempt *cur;
02101          /* Ring everyone who shares this best metric (for ringall) */
02102          for (cur = outgoing; cur; cur = cur->q_next) {
02103             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02104                if (option_debug)
02105                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02106                ret |= ring_entry(qe, cur, busies);
02107             }
02108          }
02109       } else {
02110          /* Ring just the best channel */
02111          if (option_debug)
02112             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02113          ret = ring_entry(qe, best, busies);
02114       }
02115    }
02116 
02117    return ret;
02118 }

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 2202 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(), ast_channel::uniqueid, and VERBOSE_PREFIX_3.

02203 {
02204    if (option_verbose > 2)
02205       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02206 
02207    /* Stop ringing, and resume MOH if specified */
02208    if (qe->ring_when_ringing) {
02209       ast_indicate(qe->chan, -1);
02210       ast_moh_start(qe->chan, qe->moh, NULL);
02211    }
02212 
02213    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02214    if (qe->parent->autopause && pause) {
02215       if (!set_member_paused(qe->parent->name, interface, 1)) {
02216          if (option_verbose > 2)
02217             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02218       } else {
02219          if (option_verbose > 2)
02220             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02221       }
02222    }
02223    return;
02224 }

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

Definition at line 3846 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, ast_queue_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, ast_channel::name, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, RES_OKAY, and ast_channel::uniqueid.

Referenced by load_module().

03847 {
03848    int res=-1;
03849    struct ast_module_user *lu;
03850    char *parse, *temppos = NULL;
03851    int priority_jump = 0;
03852    AST_DECLARE_APP_ARGS(args,
03853       AST_APP_ARG(queuename);
03854       AST_APP_ARG(interface);
03855       AST_APP_ARG(options);
03856    );
03857 
03858 
03859    if (ast_strlen_zero(data)) {
03860       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03861       return -1;
03862    }
03863 
03864    parse = ast_strdupa(data);
03865 
03866    AST_STANDARD_APP_ARGS(args, parse);
03867 
03868    lu = ast_module_user_add(chan);
03869 
03870    if (ast_strlen_zero(args.interface)) {
03871       args.interface = ast_strdupa(chan->name);
03872       temppos = strrchr(args.interface, '-');
03873       if (temppos)
03874          *temppos = '\0';
03875    }
03876 
03877    if (args.options) {
03878       if (strchr(args.options, 'j'))
03879          priority_jump = 1;
03880    }
03881 
03882    switch (remove_from_queue(args.queuename, args.interface)) {
03883    case RES_OKAY:
03884       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03885       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03886       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03887       res = 0;
03888       break;
03889    case RES_EXISTS:
03890       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03891       if (priority_jump || ast_opt_priority_jumping)
03892          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03893       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03894       res = 0;
03895       break;
03896    case RES_NOSUCHQUEUE:
03897       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03898       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03899       res = 0;
03900       break;
03901    case RES_NOT_DYNAMIC:
03902       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03903       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03904       res = 0;
03905       break;
03906    }
03907 
03908    ast_module_user_remove(lu);
03909 
03910    return res;
03911 }

static void rr_dep_warning ( void   )  [static]

Definition at line 469 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

00470 {
00471    static unsigned int warned = 0;
00472 
00473    if (!warned) {
00474       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00475       warned = 1;
00476    }
00477 }

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 1165 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 update_realtime_members().

01166 {
01167    struct member *m, tmpmem;
01168    int penalty = 0;
01169    int paused  = 0;
01170 
01171    if (penalty_str) {
01172       penalty = atoi(penalty_str);
01173       if (penalty < 0)
01174          penalty = 0;
01175    }
01176 
01177    if (paused_str) {
01178       paused = atoi(paused_str);
01179       if (paused < 0)
01180          paused = 0;
01181    }
01182 
01183    /* Find the member, or the place to put a new one. */
01184    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01185    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01186 
01187    /* Create a new one if not found, else update penalty */
01188    if (!m) {
01189       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01190          m->dead = 0;
01191          m->realtime = 1;
01192          add_to_interfaces(m->state_interface);
01193          ao2_link(q->members, m);
01194          ao2_ref(m, -1);
01195          m = NULL;
01196          q->membercount++;
01197       }
01198    } else {
01199       m->dead = 0;   /* Do not delete this one. */
01200       if (paused_str)
01201          m->paused = paused;
01202       if (strcasecmp(state_interface, m->state_interface)) {
01203          remove_from_interfaces(m->state_interface);
01204          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01205          add_to_interfaces(m->state_interface);
01206       }
01207       m->penalty = penalty;
01208       ao2_ref(m, -1);
01209    }
01210 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02145 {
02146    int res = 0;
02147    time_t now;
02148 
02149    /* Get the current time */
02150    time(&now);
02151 
02152    /* Check to see if it is time to announce */
02153    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02154       return 0;
02155 
02156    /* Stop the music on hold so we can play our own file */
02157    ast_moh_stop(qe->chan);
02158 
02159    if (option_verbose > 2)
02160       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02161 
02162    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02163    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02164       qe->last_periodic_announce_sound = 0;
02165    }
02166    
02167    /* play the announcement */
02168    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02169 
02170    if (res > 0 && !valid_exit(qe, res))
02171       res = 0;
02172 
02173    /* Resume Music on Hold if the caller is going to stay in the queue */
02174    if (!res)
02175       ast_moh_start(qe->chan, qe->moh, NULL);
02176 
02177    /* update last_periodic_announce_time */
02178    qe->last_periodic_announce_time = now;
02179 
02180    /* Update the current periodic announcement to the next announcement */
02181    qe->last_periodic_announce_sound++;
02182    
02183    return res;
02184 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1594 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, AST_DIGIT_ANY, ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verbose(), queue_ent::chan, call_queue::holdtime, ast_channel::language, queue_ent::last_pos, queue_ent::last_pos_said, queue_ent::moh, ast_channel::name, call_queue::name, option_verbose, queue_ent::parent, play_file(), queue_ent::pos, call_queue::roundingseconds, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, queue_ent::start, valid_exit(), and VERBOSE_PREFIX_3.

Referenced by queue_exec(), and wait_our_turn().

01595 {
01596    int res = 0, avgholdmins, avgholdsecs;
01597    time_t now;
01598 
01599    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01600    time(&now);
01601    if ((now - qe->last_pos) < 15)
01602       return 0;
01603 
01604    /* If either our position has changed, or we are over the freq timer, say position */
01605    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01606       return 0;
01607 
01608    ast_moh_stop(qe->chan);
01609    /* Say we're next, if we are */
01610    if (qe->pos == 1) {
01611       res = play_file(qe->chan, qe->parent->sound_next);
01612       if (res)
01613          goto playout;
01614       else
01615          goto posout;
01616    } else {
01617       res = play_file(qe->chan, qe->parent->sound_thereare);
01618       if (res)
01619          goto playout;
01620       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01621       if (res)
01622          goto playout;
01623       res = play_file(qe->chan, qe->parent->sound_calls);
01624       if (res)
01625          goto playout;
01626    }
01627    /* Round hold time to nearest minute */
01628    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01629 
01630    /* If they have specified a rounding then round the seconds as well */
01631    if (qe->parent->roundingseconds) {
01632       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01633       avgholdsecs *= qe->parent->roundingseconds;
01634    } else {
01635       avgholdsecs = 0;
01636    }
01637 
01638    if (option_verbose > 2)
01639       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01640 
01641    /* If the hold time is >1 min, if it's enabled, and if it's not
01642       supposed to be only once and we have already said it, say it */
01643     if ((avgholdmins+avgholdsecs) > 0 && qe->parent->announceholdtime &&
01644         ((qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE && !qe->last_pos) ||
01645         !(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE))) {
01646       res = play_file(qe->chan, qe->parent->sound_holdtime);
01647       if (res)
01648          goto playout;
01649 
01650       if (avgholdmins > 0) {
01651          if (avgholdmins < 2) {
01652             res = play_file(qe->chan, qe->parent->sound_lessthan);
01653             if (res)
01654                goto playout;
01655 
01656             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01657             if (res)
01658                goto playout;
01659          } else {
01660             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01661             if (res)
01662                goto playout;
01663          }
01664          
01665          res = play_file(qe->chan, qe->parent->sound_minutes);
01666          if (res)
01667             goto playout;
01668       }
01669       if (avgholdsecs>0) {
01670          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01671          if (res)
01672             goto playout;
01673 
01674          res = play_file(qe->chan, qe->parent->sound_seconds);
01675          if (res)
01676             goto playout;
01677       }
01678 
01679    }
01680 
01681 posout:
01682    if (option_verbose > 2)
01683       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01684          qe->chan->name, qe->parent->name, qe->pos);
01685    res = play_file(qe->chan, qe->parent->sound_thanks);
01686 
01687 playout:
01688 
01689    if ((res > 0 && !valid_exit(qe, res)))
01690       res = 0;
01691 
01692    /* Set our last_pos indicators */
01693    qe->last_pos = now;
01694    qe->last_pos_said = qe->pos;
01695 
01696    /* Don't restart music on hold if we're about to exit the caller from the queue */
01697    if (!res)
01698       ast_moh_start(qe->chan, qe->moh, NULL);
01699 
01700    return res;
01701 }

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

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

03588 {
03589    int found = 0;
03590    struct call_queue *q;
03591    struct member *mem;
03592 
03593    /* Special event for when all queues are paused - individual events still generated */
03594    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03595    if (ast_strlen_zero(queuename))
03596       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03597 
03598    AST_LIST_LOCK(&queues);
03599    AST_LIST_TRAVERSE(&queues, q, list) {
03600       ao2_lock(q);
03601       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03602          if ((mem = interface_exists(q, interface))) {
03603             found++;
03604             if (mem->paused == paused)
03605                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03606             mem->paused = paused;
03607 
03608             if (queue_persistent_members)
03609                dump_queue_members(q);
03610 
03611             if (mem->realtime)
03612                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03613 
03614             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03615 
03616             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03617                "Queue: %s\r\n"
03618                "Location: %s\r\n"
03619                "MemberName: %s\r\n"
03620                "Paused: %d\r\n",
03621                   q->name, mem->interface, mem->membername, paused);
03622             ao2_ref(mem, -1);
03623          }
03624       }
03625       ao2_unlock(q);
03626    }
03627    AST_LIST_UNLOCK(&queues);
03628 
03629    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03630 }

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

sets the QUEUESTATUS channel variable

Definition at line 488 of file app_queue.c.

References queue_ent::chan, pbx_builtin_setvar_helper(), queue_results, and text.

Referenced by queue_exec().

00489 {
00490    int i;
00491 
00492    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00493       if (queue_results[i].id == res) {
00494          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00495          return;
00496       }
00497    }
00498 }

static struct ast_datastore* setup_transfer_datastore ( struct queue_ent qe,
struct member member,
time_t  starttime,
int  callcompletedinsl 
) [static]

create a datastore for storing relevant info to log attended transfers in the queue_log

Definition at line 2778 of file app_queue.c.

References ast_calloc, ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_lock, ast_channel_unlock, ast_log(), queue_ent::chan, ast_datastore::data, LOG_WARNING, queue_transfer_ds::member, queue_transfer_ds::qe, and queue_transfer_info.

02779 {
02780    struct ast_datastore *ds;
02781    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02782 
02783    if (!qtds) {
02784       ast_log(LOG_WARNING, "Memory allocation error!\n");
02785       return NULL;
02786    }
02787 
02788    ast_channel_lock(qe->chan);
02789    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02790       ast_channel_unlock(qe->chan);
02791       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02792       return NULL;
02793    }
02794 
02795    qtds->qe = qe;
02796    /* This member is refcounted in try_calling, so no need to add it here, too */
02797    qtds->member = member;
02798    qtds->starttime = starttime;
02799    qtds->callcompletedinsl = callcompletedinsl;
02800    ds->data = qtds;
02801    ast_channel_datastore_add(qe->chan, ds);
02802    ast_channel_unlock(qe->chan);
02803    return ds;
02804 }

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

Producer of the statechange queue.

Definition at line 776 of file app_queue.c.

References ast_calloc, ast_cond_signal(), AST_LIST_INSERT_TAIL, ast_mutex_lock(), ast_mutex_unlock(), device_state, and statechange::entry.

Referenced by load_module(), and unload_module().

00777 {
00778    struct statechange *sc;
00779 
00780    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00781       return 0;
00782 
00783    sc->state = state;
00784    strcpy(sc->dev, dev);
00785 
00786    ast_mutex_lock(&device_state.lock);
00787    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00788    ast_cond_signal(&device_state.cond);
00789    ast_mutex_unlock(&device_state.lock);
00790 
00791    return 0;
00792 }

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

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

02121 {
02122    struct callattempt *best = find_best(outgoing);
02123 
02124    if (best) {
02125       /* Ring just the best channel */
02126       if (option_debug)
02127          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02128       qe->parent->rrpos = best->metric % 1000;
02129    } else {
02130       /* Just increment rrpos */
02131       if (qe->parent->wrapped) {
02132          /* No more channels, start over */
02133          qe->parent->rrpos = 0;
02134       } else {
02135          /* Prioritize next entry */
02136          qe->parent->rrpos++;
02137       }
02138    }
02139    qe->parent->wrapped = 0;
02140 
02141    return 0;
02142 }

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

Definition at line 512 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

00513 {
00514    int x;
00515 
00516    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00517       if (!strcasecmp(strategy, strategies[x].name))
00518          return strategies[x].strategy;
00519    }
00520 
00521    return -1;
00522 }

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

References ast_channel::_state, queue_ent::announce, ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_calloc, AST_CDR_FLAG_DONT_TOUCH, AST_CDR_FLAG_POST_DISABLED, ast_cdr_isset_unanswered(), ast_cdr_noanswer(), 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_unlock, ast_copy_string(), AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, 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_set_flag, AST_STATE_UP, ast_strlen_zero(), ast_test_flag, calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, ast_cdr::dstchannel, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, free, ast_datastore::inheritance, member::interface, member::lastcall, ast_dialed_interface::list, LOG_DEBUG, LOG_NOTICE, manager_event(), call_queue::membercount, call_queue::members, call_queue::name, ast_channel::name, option_debug, queue_ent::parent, queue_ent::pending, callattempt::q_next, QUEUE_STRATEGY_ROUNDROBIN, ring_one(), member::ringcount, call_queue::ringlimit, queue_ent::start, member::status, store_next(), call_queue::strategy, call_queue::timeout, queue_ent::tries, ast_channel::uniqueid, and wait_for_answer().

Referenced by queue_exec().

02832 {
02833    struct member *cur;
02834    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02835    int to;
02836    char oldexten[AST_MAX_EXTENSION]="";
02837    char oldcontext[AST_MAX_CONTEXT]="";
02838    char queuename[256]="";
02839    struct ast_channel *peer;
02840    struct ast_channel *which;
02841    struct callattempt *lpeer;
02842    struct member *member;
02843    struct ast_app *app;
02844    int res = 0, bridge = 0;
02845    int numbusies = 0;
02846    int x=0;
02847    char *announce = NULL;
02848    char digit = 0;
02849    time_t callstart;
02850    time_t now = time(NULL);
02851    struct ast_bridge_config bridge_config;
02852    char nondataquality = 1;
02853    char *agiexec = NULL;
02854    int ret = 0;
02855    const char *monitorfilename;
02856    const char *monitor_exec;
02857    const char *monitor_options;
02858    char tmpid[256], tmpid2[256];
02859    char meid[1024], meid2[1024];
02860    char mixmonargs[1512];
02861    struct ast_app *mixmonapp = NULL;
02862    char *p;
02863    char vars[2048];
02864    int forwardsallowed = 1;
02865    int callcompletedinsl;
02866    struct ao2_iterator memi;
02867    struct ast_datastore *datastore, *transfer_ds;
02868 
02869    ast_channel_lock(qe->chan);
02870    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02871    ast_channel_unlock(qe->chan);
02872 
02873    memset(&bridge_config, 0, sizeof(bridge_config));
02874    time(&now);
02875 
02876    /* If we've already exceeded our timeout, then just stop
02877     * This should be extremely rare. queue_exec will take care
02878     * of removing the caller and reporting the timeout as the reason.
02879     */
02880    if (qe->expire && now >= qe->expire) {
02881       res = 0;
02882       goto out;
02883    }
02884       
02885    for (; options && *options; options++)
02886       switch (*options) {
02887       case 't':
02888          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02889          break;
02890       case 'T':
02891          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02892          break;
02893       case 'w':
02894          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02895          break;
02896       case 'W':
02897          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02898          break;
02899       case 'd':
02900          nondataquality = 0;
02901          break;
02902       case 'h':
02903          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02904          break;
02905       case 'H':
02906          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02907          break;
02908       case 'n':
02909          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02910             (*tries)++;
02911          else
02912             *tries = qe->parent->membercount;
02913          *noption = 1;
02914          break;
02915       case 'i':
02916          forwardsallowed = 0;
02917          break;
02918       }
02919 
02920    /* Hold the lock while we setup the outgoing calls */
02921    if (use_weight)
02922       AST_LIST_LOCK(&queues);
02923    ao2_lock(qe->parent);
02924    if (option_debug)
02925       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02926                      qe->chan->name);
02927    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02928    if (!ast_strlen_zero(qe->announce))
02929       announce = qe->announce;
02930    if (!ast_strlen_zero(announceoverride))
02931       announce = announceoverride;
02932 
02933    memi = ao2_iterator_init(qe->parent->members, 0);
02934    while ((cur = ao2_iterator_next(&memi))) {
02935       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02936       struct ast_dialed_interface *di;
02937       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
02938       if (!tmp) {
02939          ao2_iterator_destroy(&memi);
02940          ao2_ref(cur, -1);
02941          ao2_unlock(qe->parent);
02942          if (use_weight)
02943             AST_LIST_UNLOCK(&queues);
02944          goto out;
02945       }
02946       if (!datastore) {
02947          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
02948             ao2_iterator_destroy(&memi);
02949             ao2_ref(cur, -1);
02950             ao2_unlock(qe->parent);
02951             if (use_weight)
02952                AST_LIST_UNLOCK(&queues);
02953             free(tmp);
02954             goto out;
02955          }
02956          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
02957          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
02958             ao2_iterator_destroy(&memi);
02959             ao2_ref(cur, -1);
02960             ao2_unlock(qe->parent);
02961             if (use_weight)
02962                AST_LIST_UNLOCK(&queues);
02963             free(tmp);
02964             goto out;
02965          }
02966          datastore->data = dialed_interfaces;
02967          AST_LIST_HEAD_INIT(dialed_interfaces);
02968 
02969          ast_channel_lock(qe->chan);
02970          ast_channel_datastore_add(qe->chan, datastore);
02971          ast_channel_unlock(qe->chan);
02972       } else
02973          dialed_interfaces = datastore->data;
02974 
02975       AST_LIST_LOCK(dialed_interfaces);
02976       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
02977          if (!strcasecmp(cur->interface, di->interface)) {
02978             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
02979                di->interface);
02980             break;
02981          }
02982       }
02983       AST_LIST_UNLOCK(dialed_interfaces);
02984       
02985       if (di) {
02986          free(tmp);
02987          continue;
02988       }
02989 
02990       /* It is always ok to dial a Local interface.  We only keep track of
02991        * which "real" interfaces have been dialed.  The Local channel will
02992        * inherit this list so that if it ends up dialing a real interface,
02993        * it won't call one that has already been called. */
02994       if (strncasecmp(cur->interface, "Local/", 6)) {
02995          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
02996             ao2_iterator_destroy(&memi);
02997             ao2_ref(cur, -1);
02998             ao2_unlock(qe->parent);
02999             if (use_weight)
03000                AST_LIST_UNLOCK(&queues);
03001             free(tmp);
03002             goto out;
03003          }
03004          strcpy(di->interface, cur->interface);
03005 
03006          AST_LIST_LOCK(dialed_interfaces);
03007          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
03008          AST_LIST_UNLOCK(dialed_interfaces);
03009       }
03010 
03011       tmp->stillgoing = -1;
03012       tmp->member = cur;
03013       tmp->oldstatus = cur->status;
03014       tmp->lastcall = cur->lastcall;
03015       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
03016       if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
03017          cur->ringcount = 0;
03018       }
03019       /* Special case: If we ring everyone, go ahead and ring them, otherwise
03020          just calculate their metric for the appropriate strategy */
03021       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
03022          /* Put them in the list of outgoing thingies...  We're ready now.
03023             XXX If we're forcibly removed, these outgoing calls won't get
03024             hung up XXX */
03025          tmp->q_next = outgoing;
03026          outgoing = tmp;      
03027          /* If this line is up, don't try anybody else */
03028          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
03029             break;
03030       } else {
03031          ao2_ref(cur, -1);
03032          free(tmp);
03033       }
03034    }
03035    ao2_iterator_destroy(&memi);
03036    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
03037       to = (qe->expire - now) * 1000;
03038    else
03039       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
03040    ++qe->pending;
03041    ++qe->tries;
03042    if (option_debug)
03043       ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
03044                   qe->chan->name, queuename, qe->tries);
03045    ao2_unlock(qe->parent);
03046    ring_one(qe, outgoing, &numbusies);
03047    if (use_weight)
03048       AST_LIST_UNLOCK(&queues);
03049    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
03050    /* The ast_channel_datastore_remove() function could fail here if the
03051     * datastore was moved to another channel during a masquerade. If this is
03052     * the case, don't free the datastore here because later, when the channel
03053     * to which the datastore was moved hangs up, it will attempt to free this
03054     * datastore again, causing a crash
03055     */
03056    ast_channel_lock(qe->chan);
03057    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
03058       ast_channel_datastore_free(datastore);
03059    }
03060    ast_channel_unlock(qe->chan);
03061    ao2_lock(qe->parent);
03062    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
03063       store_next(qe, outgoing);
03064    }
03065    ao2_unlock(qe->parent);
03066    peer = lpeer ? lpeer->chan : NULL;
03067    if (!peer) {
03068       qe->pending = 0;
03069       if (to) {
03070          /* Must gotten hung up */
03071          res = -1;
03072       } else {
03073          /* User exited by pressing a digit */
03074          res = digit;
03075       }
03076       if (res == -1) {
03077          /* Post this CDR, and mark call as NOANSWER */
03078          ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_DONT_TOUCH);
03079          ast_cdr_noanswer(qe->chan->cdr);
03080          if (queue_debug)
03081             ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
03082       }
03083       if (qe->parent->eventwhencalled) {
03084          manager_event(EVENT_FLAG_AGENT, "AgentTimeout",
03085                               "Queue: %s\r\n"
03086                               "ChannelCalling: %s\r\n"
03087                               "Uniqueid: %s\r\n"
03088                               "Tries: %d\r\n"
03089                               "Holdtime: %ld\r\n",
03090                               queuename, qe->chan->name, qe->chan->uniqueid, qe->tries,
03091                               (long)time(NULL) - qe->start);
03092       }
03093       if (ast_cdr_isset_unanswered()) {
03094          /* channel contains the name of one of the outgoing channels
03095             in its CDR; zero out this CDR to avoid a dual-posting */
03096          struct callattempt *o;
03097          for (o = outgoing; o; o = o->q_next) {
03098             if (!o->chan) {
03099                continue;
03100             }
03101             if (strcmp(o->chan->cdr->dstchannel, qe->chan->cdr->dstchannel) == 0) {
03102                ast_set_flag(o->chan->cdr, AST_CDR_FLAG_POST_DISABLED);
03103                break;
03104             }
03105          }
03106       }
03107    } else { /* peer is valid */
03108       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
03109          we will always return with -1 so that it is hung up properly after the
03110          conversation.  */
03111       if (!strcmp(qe->chan->tech->type, "Zap"))
03112          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03113       if (!strcmp(peer->tech->type, "Zap"))
03114          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
03115       /* Update parameters for the queue */
03116       time(&now);
03117       recalc_holdtime(qe, (now - qe->start));
03118       ao2_lock(qe->parent);
03119       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
03120       ao2_unlock(qe->parent);
03121       member = lpeer->member;
03122       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
03123       ao2_ref(member, 1);
03124       hangupcalls(outgoing, peer);
03125       outgoing = NULL;
03126       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
03127          int res2;
03128 
03129          res2 = ast_autoservice_start(qe->chan);
03130          if (!res2) {
03131             if (qe->parent->memberdelay) {
03132                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
03133                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
03134             }
03135             if (!res2 && announce) {
03136                play_file(peer, announce);
03137             }
03138             if (!res2 && qe->parent->reportholdtime) {
03139                if (!play_file(peer, qe->parent->sound_reporthold)) {
03140                   int holdtime;
03141 
03142                   time(&now);
03143                   holdtime = abs((now - qe->start) / 60);
03144                   if (holdtime < 2) {
03145                      play_file(peer, qe->parent->sound_lessthan);
03146                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
03147                   } else
03148                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
03149                   play_file(peer, qe->parent->sound_minutes);
03150                }
03151             }
03152          }
03153          res2 |= ast_autoservice_stop(qe->chan);
03154          if (peer->_softhangup) {
03155             /* Agent must have hung up */
03156             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
03157             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
03158             if (qe->parent->eventwhencalled)
03159                manager_event(EVENT_FLAG_AGENT, "AgentDump",
03160                      "Queue: %s\r\n"
03161                      "Uniqueid: %s\r\n"
03162                      "Channel: %s\r\n"
03163                      "Member: %s\r\n"
03164                      "MemberName: %s\r\n"
03165                      "%s",
03166                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03167                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03168             ast_hangup(peer);
03169             ao2_ref(member, -1);
03170             goto out;
03171          } else if (res2) {
03172             /* Caller must have hung up just before being connected*/
03173             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
03174             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
03175             record_abandoned(qe);
03176             ast_cdr_noanswer(qe->chan->cdr);
03177             ast_hangup(peer);
03178             ao2_ref(member, -1);
03179             return -1;
03180          }
03181       }
03182       /* Stop music on hold */
03183       ast_moh_stop(qe->chan);
03184       /* If appropriate, log that we have a destination channel */
03185       if (qe->chan->cdr)
03186          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
03187       /* Make sure channels are compatible */
03188       res = ast_channel_make_compatible(qe->chan, peer);
03189       if (res < 0) {
03190          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
03191          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
03192          record_abandoned(qe);
03193          ast_cdr_failed(qe->chan->cdr);
03194          ast_hangup(peer);
03195          ao2_ref(member, -1);
03196          return -1;
03197       }
03198 
03199       if (qe->parent->setinterfacevar)
03200             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
03201 
03202       /* Begin Monitoring */
03203       if (qe->parent->monfmt && *qe->parent->monfmt) {
03204          if (!qe->parent->montype) {
03205             if (option_debug)
03206                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
03207             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03208             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
03209                which = qe->chan;
03210             else
03211                which = peer;
03212             if (monitorfilename)
03213                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
03214             else if (qe->chan->cdr)
03215                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
03216             else {
03217                /* Last ditch effort -- no CDR, make up something */
03218                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03219                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
03220             }
03221             if (qe->parent->monjoin)
03222                ast_monitor_setjoinfiles(which, 1);
03223          } else {
03224             if (option_debug)
03225                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
03226             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
03227             if (!monitorfilename) {
03228                if (qe->chan->cdr)
03229                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
03230                else
03231                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
03232             } else {
03233                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
03234                for (p = tmpid2; *p ; p++) {
03235                   if (*p == '^' && *(p+1) == '{') {
03236                      *p = '$';
03237                   }
03238                }
03239 
03240                memset(tmpid, 0, sizeof(tmpid));
03241                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
03242             }
03243 
03244             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
03245             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
03246 
03247             if (monitor_exec) {
03248                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
03249                for (p = meid2; *p ; p++) {
03250                   if (*p == '^' && *(p+1) == '{') {
03251                      *p = '$';
03252                   }
03253                }
03254 
03255                memset(meid, 0, sizeof(meid));
03256                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
03257             }
03258    
03259             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
03260 
03261             mixmonapp = pbx_findapp("MixMonitor");
03262 
03263             if (strchr(tmpid2, '|')) {
03264                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
03265                mixmonapp = NULL;
03266             }
03267 
03268             if (!monitor_options)
03269                monitor_options = "";
03270             
03271             if (strchr(monitor_options, '|')) {
03272                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
03273                mixmonapp = NULL;
03274             }
03275 
03276             if (mixmonapp) {
03277                if (!ast_strlen_zero(monitor_exec))
03278                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
03279                else
03280                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
03281                   
03282                if (option_debug)
03283                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03284                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03285                if (qe->chan->cdr)
03286                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03287                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03288                if (qe->chan->cdr)
03289                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03290 
03291             } else
03292                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03293 
03294          }
03295       }
03296       /* Drop out of the queue at this point, to prepare for next caller */
03297       leave_queue(qe);        
03298       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03299          if (option_debug)
03300             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
03301          ast_channel_sendurl(peer, url);
03302       }
03303       if (!ast_strlen_zero(agi)) {
03304          if (option_debug)
03305             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
03306          app = pbx_findapp("agi");
03307          if (app) {
03308             agiexec = ast_strdupa(agi);
03309             ret = pbx_exec(qe->chan, app, agiexec);
03310          } else
03311             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03312       }
03313       qe->handled++;
03314       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
03315       if (qe->parent->eventwhencalled)
03316          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03317                "Queue: %s\r\n"
03318                "Uniqueid: %s\r\n"
03319                "Channel: %s\r\n"
03320                "Member: %s\r\n"
03321                "MemberName: %s\r\n"
03322                "Holdtime: %ld\r\n"
03323                "BridgedChannel: %s\r\n"
03324                "%s",
03325                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03326                (long)time(NULL) - qe->start, peer->uniqueid,
03327                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03328       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03329       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03330       time(&callstart);
03331 
03332       if (member->status == AST_DEVICE_NOT_INUSE)
03333          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);
03334          
03335       transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
03336       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03337 
03338       ast_channel_lock(qe->chan);
03339       if (!attended_transfer_occurred(qe->chan)) {
03340          struct ast_datastore *tds;
03341 
03342          /* detect a blind transfer */
03343          if (!(qe->chan->_softhangup | peer->_softhangup) && (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten))) {
03344             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03345                qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03346                (long) (time(NULL) - callstart));
03347          } else if (qe->chan->_softhangup) {
03348             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03349                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03350             if (qe->parent->eventwhencalled)
03351                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03352                      "Queue: %s\r\n"
03353                      "Uniqueid: %s\r\n"
03354                      "Channel: %s\r\n"
03355                      "Member: %s\r\n"
03356                      "MemberName: %s\r\n"
03357                      "HoldTime: %ld\r\n"
03358                      "TalkTime: %ld\r\n"
03359                      "Reason: caller\r\n"
03360                      "%s",
03361                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03362                      (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03363                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03364          } else {
03365             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03366                (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03367             if (qe->parent->eventwhencalled)
03368                manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03369                      "Queue: %s\r\n"
03370                      "Uniqueid: %s\r\n"
03371                      "Channel: %s\r\n"
03372                      "MemberName: %s\r\n"
03373                      "HoldTime: %ld\r\n"
03374                      "TalkTime: %ld\r\n"
03375                      "Reason: agent\r\n"
03376                      "%s",
03377                      queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
03378                      (long)(time(NULL) - callstart),
03379                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03380          }
03381          if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {  
03382             ast_channel_datastore_remove(qe->chan, tds);
03383          }
03384          update_queue(qe->parent, member, callcompletedinsl);
03385       }
03386 
03387       if (transfer_ds) {
03388          ast_channel_datastore_free(transfer_ds);
03389       }
03390       ast_channel_unlock(qe->chan);
03391       ast_hangup(peer);
03392       res = bridge ? bridge : 1;
03393       ao2_ref(member, -1);
03394    }
03395 out:
03396    hangupcalls(outgoing, NULL);
03397 
03398    return res;
03399 }

static int unload_module ( void   )  [static]

Definition at line 5500 of file app_queue.c.

References ast_cli_unregister_multiple(), ast_cond_signal(), ast_custom_function_unregister(), ast_devstate_del(), ast_manager_unregister(), ast_module_user_hangup_all, ast_mutex_lock(), ast_mutex_unlock(), AST_PTHREADT_NULL, ast_unregister_application(), clear_and_free_interfaces(), cli_queue, device_state, queueagentcount_function, queuemembercount_function, queuememberlist_function, queuewaitingcount_function, and statechange_queue().

05501 {
05502    int res;
05503 
05504    if (device_state.thread != AST_PTHREADT_NULL) {
05505       device_state.stop = 1;
05506       ast_mutex_lock(&device_state.lock);
05507       ast_cond_signal(&device_state.cond);
05508       ast_mutex_unlock(&device_state.lock);
05509       pthread_join(device_state.thread, NULL);
05510    }
05511 
05512    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05513    res = ast_manager_unregister("QueueStatus");
05514    res |= ast_manager_unregister("Queues");
05515    res |= ast_manager_unregister("QueueAdd");
05516    res |= ast_manager_unregister("QueueRemove");
05517    res |= ast_manager_unregister("QueuePause");
05518    res |= ast_unregister_application(app_aqm);
05519    res |= ast_unregister_application(app_rqm);
05520    res |= ast_unregister_application(app_pqm);
05521    res |= ast_unregister_application(app_upqm);
05522    res |= ast_unregister_application(app_ql);
05523    res |= ast_unregister_application(app);
05524    res |= ast_custom_function_unregister(&queueagentcount_function);
05525    res |= ast_custom_function_unregister(&queuemembercount_function);
05526    res |= ast_custom_function_unregister(&queuememberlist_function);
05527    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05528    ast_devstate_del(statechange_queue, NULL);
05529 
05530    ast_module_user_hangup_all();
05531 
05532    clear_and_free_interfaces();
05533 
05534    return res;
05535 }

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

Definition at line 2631 of file app_queue.c.

References ao2_lock(), ao2_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, and member::lastcall.

Referenced by queue_transfer_fixup().

02632 {
02633    ao2_lock(q);
02634    time(&member->lastcall);
02635    member->calls++;
02636    q->callscompleted++;
02637    if (callcompletedinsl)
02638       q->callscompletedinsl++;
02639    ao2_unlock(q);
02640    return 0;
02641 }

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

Definition at line 1356 of file app_queue.c.

References ast_load_realtime(), ast_strlen_zero(), ast_update_realtime(), ast_variables_destroy(), member::interface, and var.

Referenced by set_member_paused().

01357 {
01358    struct ast_variable *var, *save;
01359    int ret = -1;
01360 
01361    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01362       return ret;
01363    save = var;
01364    while (var) {
01365       if (!strcmp(var->name, "uniqueid"))
01366          break;
01367       var = var->next;
01368    }
01369    if (var && !ast_strlen_zero(var->value)) {
01370       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01371          ret = 0;
01372    }
01373    ast_variables_destroy(save);
01374    return ret;
01375 }

static void update_realtime_members ( struct call_queue q  )  [static]

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

01378 {
01379    struct ast_config *member_config = NULL;
01380    struct member *m;
01381    char *interface = NULL;
01382    struct ao2_iterator mem_iter;
01383 
01384    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01385       /*This queue doesn't have realtime members*/
01386       if (option_debug > 2)
01387          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01388       return;
01389    }
01390 
01391    ao2_lock(q);
01392    
01393    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01394    mem_iter = ao2_iterator_init(q->members, 0);
01395    while ((m = ao2_iterator_next(&mem_iter))) {
01396       if (m->realtime)
01397          m->dead = 1;
01398       ao2_ref(m, -1);
01399    }
01400    ao2_iterator_destroy(&mem_iter);
01401 
01402    while ((interface = ast_category_browse(member_config, interface))) {
01403       rt_handle_member_record(q, interface,
01404          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01405          ast_variable_retrieve(member_config, interface, "penalty"),
01406          ast_variable_retrieve(member_config, interface, "paused"),
01407          S_OR(ast_variable_retrieve(member_config, interface, "state_interface"), interface));
01408    }
01409 
01410    /* Delete all realtime members that have been deleted in DB. */
01411    mem_iter = ao2_iterator_init(q->members, 0);
01412    while ((m = ao2_iterator_next(&mem_iter))) {
01413       if (m->dead) {
01414          ao2_unlink(q->members, m);
01415          ao2_unlock(q);
01416          remove_from_interfaces(m->state_interface);
01417          ao2_lock(q);
01418          q->membercount--;
01419       }
01420       ao2_ref(m, -1);
01421    }
01422    ao2_iterator_destroy(&mem_iter);
01423    ao2_unlock(q);
01424    ast_config_destroy(member_config);
01425 }

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

Definition at line 627 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, member_interface::list, 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().

00628 {
00629    struct member *cur;
00630    struct ao2_iterator mem_iter;
00631    struct call_queue *q;
00632    char tmp_interface[80];
00633 
00634    AST_LIST_LOCK(&queues);
00635    AST_LIST_TRAVERSE(&queues, q, list) {
00636       ao2_lock(q);
00637       mem_iter = ao2_iterator_init(q->members, 0);
00638       while ((cur = ao2_iterator_next(&mem_iter))) {
00639          char *slash_pos;
00640          ast_copy_string(tmp_interface, cur->state_interface, sizeof(tmp_interface));
00641          if ((slash_pos = strchr(tmp_interface, '/')))
00642             if ((slash_pos = strchr(slash_pos + 1, '/')))
00643                *slash_pos = '\0';
00644 
00645          if (strcasecmp(interface, tmp_interface)) {
00646             ao2_ref(cur, -1);
00647             continue;
00648          }
00649 
00650          if (cur->status != status) {
00651             cur->status = status;
00652             if (q->maskmemberstatus) {
00653                ao2_ref(cur, -1);
00654                continue;
00655             }
00656 
00657             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00658                "Queue: %s\r\n"
00659                "Location: %s\r\n"
00660                "MemberName: %s\r\n"
00661                "Membership: %s\r\n"
00662                "Penalty: %d\r\n"
00663                "CallsTaken: %d\r\n"
00664                "LastCall: %d\r\n"
00665                "Status: %d\r\n"
00666                "Paused: %d\r\n",
00667                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00668                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00669          }
00670          ao2_ref(cur, -1);
00671       }
00672       ao2_iterator_destroy(&mem_iter);
00673       ao2_unlock(q);
00674    }
00675    AST_LIST_UNLOCK(&queues);
00676 
00677    return 0;
00678 }

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

Definition at line 3788 of file app_queue.c.

References AST_APP_ARG, AST_DECLARE_APP_ARGS, ast_goto_if_exists(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_opt_priority_jumping, AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_module_user::chan, ast_channel::context, ast_channel::exten, LOG_WARNING, parse(), pbx_builtin_setvar_helper(), ast_channel::priority, and set_member_paused().

Referenced by load_module().

03789 {
03790    struct ast_module_user *lu;
03791    char *parse;
03792    int priority_jump = 0;
03793    int ignore_fail = 0;
03794    AST_DECLARE_APP_ARGS(args,
03795       AST_APP_ARG(queuename);
03796       AST_APP_ARG(interface);
03797       AST_APP_ARG(options);
03798    );
03799 
03800    if (ast_strlen_zero(data)) {
03801       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03802       return -1;
03803    }
03804 
03805    parse = ast_strdupa(data);
03806 
03807    AST_STANDARD_APP_ARGS(args, parse);
03808 
03809    lu = ast_module_user_add(chan);
03810 
03811    if (args.options) {
03812       if (strchr(args.options, 'j'))
03813          priority_jump = 1;
03814       if (strchr(args.options, 'i'))
03815          ignore_fail = 1;
03816    }
03817 
03818    if (ast_strlen_zero(args.interface)) {
03819       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03820       ast_module_user_remove(lu);
03821       return -1;
03822    }
03823 
03824    if (set_member_paused(args.queuename, args.interface, 0)) {
03825       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03826       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03827       if (priority_jump || ast_opt_priority_jumping) {
03828          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03829             ast_module_user_remove(lu);
03830             return 0;
03831          }
03832       }
03833       ast_module_user_remove(lu);
03834       if (ignore_fail) {
03835          return 0;
03836       } else {
03837          return -1;
03838       }
03839    }
03840 
03841    ast_module_user_remove(lu);
03842    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03843    return 0;
03844 }

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

Definition at line 1561 of file app_queue.c.

References ast_canmatch_extension(), ast_goto_if_exists(), ast_strlen_zero(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::context, queue_ent::digits, and queue_ent::valid_digits.

Referenced by say_periodic_announcement(), say_position(), wait_a_bit(), and wait_our_turn().

01562 {
01563    int digitlen = strlen(qe->digits);
01564 
01565    /* Prevent possible buffer overflow */
01566    if (digitlen < sizeof(qe->digits) - 2) {
01567       qe->digits[digitlen] = digit;
01568       qe->digits[digitlen + 1] = '\0';
01569    } else {
01570       qe->digits[0] = '\0';
01571       return 0;
01572    }
01573 
01574    /* If there's no context to goto, short-circuit */
01575    if (ast_strlen_zero(qe->context))
01576       return 0;
01577 
01578    /* If the extension is bad, then reset the digits to blank */
01579    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01580       qe->digits[0] = '\0';
01581       return 0;
01582    }
01583 
01584    /* We have an exact match */
01585    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01586       qe->valid_digits = 1;
01587       /* Return 1 on a successful goto */
01588       return 1;
01589    }
01590 
01591    return 0;
01592 }

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

Definition at line 1865 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry().

01866 {
01867    char *tmp = alloca(len);
01868 
01869    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01870       int i, j;
01871 
01872       /* convert "\n" to "\nVariable: " */
01873       strcpy(vars, "Variable: ");
01874 
01875       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01876          vars[j] = tmp[i];
01877 
01878          if (tmp[i + 1] == '\0')
01879             break;
01880          if (tmp[i] == '\n') {
01881             vars[j++] = '\r';
01882             vars[j++] = '\n';
01883 
01884             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01885             j += 9;
01886          }
01887       }
01888       if (j > len - 3)
01889          j = len - 3;
01890       vars[j++] = '\r';
01891       vars[j++] = '\n';
01892       vars[j] = '\0';
01893    } else {
01894       /* there are no channel variables; leave it blank */
01895       *vars = '\0';
01896    }
01897    return vars;
01898 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3401 of file app_queue.c.

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

Referenced by queue_exec().

03402 {
03403    /* Don't need to hold the lock while we setup the outgoing calls */
03404    int retrywait = qe->parent->retry * 1000;
03405 
03406    int res = ast_waitfordigit(qe->chan, retrywait);
03407    if (res > 0 && !valid_exit(qe, res))
03408       res = 0;
03409 
03410    return res;
03411 }

static struct callattempt* wait_for_answer ( struct queue_ent qe,
struct callattempt outgoing,
int *  to,
char *  digit,
int  prebusies,
int  caller_disconnect,
int  forwardsallowed 
) [static]

Wait for a member to answer the call.

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

References AST_MAX_WATCHERS, callattempt::call_next, callattempt::chan, queue_ent::chan, f, call_queue::name, queue_ent::parent, callattempt::q_next, QUEUE_STRATEGY_RINGALL, ring_one(), starttime, callattempt::stillgoing, and call_queue::strategy.

02238 {
02239    char *queue = qe->parent->name;
02240    struct callattempt *o, *start = NULL, *prev = NULL;
02241    int status;
02242    int numbusies = prebusies;
02243    int numnochan = 0;
02244    int stillgoing = 0;
02245    int orig = *to;
02246    struct ast_frame *f;
02247    struct callattempt *peer = NULL;
02248    struct ast_channel *winner;
02249    struct ast_channel *in = qe->chan;
02250    char on[80] = "";
02251    char membername[80] = "";
02252    long starttime = 0;
02253    long endtime = 0; 
02254 
02255    starttime = (long) time(NULL);
02256    
02257    while (*to && !peer) {
02258       int numlines, retry, pos = 1;
02259       struct ast_channel *watchers[AST_MAX_WATCHERS];
02260       watchers[0] = in;
02261       start = NULL;
02262 
02263       for (retry = 0; retry < 2; retry++) {
02264          numlines = 0;
02265          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02266             if (o->stillgoing) { /* Keep track of important channels */
02267                stillgoing = 1;
02268                if (o->chan) {
02269                   watchers[pos++] = o->chan;
02270                   if (!start)
02271                      start = o;
02272                   else
02273                      prev->call_next = o;
02274                   prev = o;
02275                }
02276             }
02277             numlines++;
02278          }
02279          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02280             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02281             break;
02282          /* On "ringall" strategy we only move to the next penalty level
02283             when *all* ringing phones are done in the current penalty level */
02284          ring_one(qe, outgoing, &numbusies);
02285          /* and retry... */
02286       }
02287       if (pos == 1 /* not found */) {
02288          if (numlines == (numbusies + numnochan)) {
02289             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02290          } else {
02291             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02292          }
02293          *to = 0;
02294          return NULL;
02295       }
02296 
02297       /* Poll for events from both the incoming channel as well as any outgoing channels */
02298       winner = ast_waitfor_n(watchers, pos, to);
02299 
02300       /* Service all of the outgoing channels */
02301       for (o = start; o; o = o->call_next) {
02302          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02303             if (!peer) {
02304                if (option_verbose > 2)
02305                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02306                peer = o;
02307             }
02308          } else if (o->chan && (o->chan == winner)) {
02309 
02310             ast_copy_string(on, o->member->interface, sizeof(on));
02311             ast_copy_string(membername, o->member->membername, sizeof(membername));
02312 
02313             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02314                if (option_verbose > 2)
02315                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02316                numnochan++;
02317                do_hang(o);
02318                winner = NULL;
02319                continue;
02320             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02321                char tmpchan[256];
02322                char *stuff;
02323                char *tech;
02324 
02325                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02326                if ((stuff = strchr(tmpchan, '/'))) {
02327                   *stuff++ = '\0';
02328                   tech = tmpchan;
02329                } else {
02330                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02331                   stuff = tmpchan;
02332                   tech = "Local";
02333                }
02334                /* Before processing channel, go ahead and check for forwarding */
02335                if (option_verbose > 2)
02336                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02337                /* Setup parameters */
02338                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02339                if (!o->chan) {
02340                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02341                   o->stillgoing = 0;
02342                   numnochan++;
02343                } else {
02344                   ast_channel_inherit_variables(in, o->chan);
02345                   ast_channel_datastore_inherit(in, o->chan);
02346                   if (o->chan->cid.cid_num)
02347                      free(o->chan->cid.cid_num);
02348                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02349 
02350                   if (o->chan->cid.cid_name)
02351                      free(o->chan->cid.cid_name);
02352                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02353 
02354                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02355                   o->chan->cdrflags = in->cdrflags;
02356 
02357                   if (in->cid.cid_ani) {
02358                      if (o->chan->cid.cid_ani)
02359                         free(o->chan->cid.cid_ani);
02360                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02361                   }
02362                   if (o->chan->cid.cid_rdnis)
02363                      free(o->chan->cid.cid_rdnis);
02364                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02365                   if (ast_call(o->chan, tmpchan, 0)) {
02366                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02367                      do_hang(o);
02368                      numnochan++;
02369                   }
02370                }
02371                /* Hangup the original channel now, in case we needed it */
02372                ast_hangup(winner);
02373                continue;
02374             }
02375             f = ast_read(winner);
02376             if (f) {
02377                if (f->frametype == AST_FRAME_CONTROL) {
02378                   switch (f->subclass) {
02379                   case AST_CONTROL_ANSWER:
02380                      /* This is our guy if someone answered. */
02381                      if (!peer) {
02382                         if (option_verbose > 2)
02383                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02384                         peer = o;
02385                      }
02386                      break;
02387                   case AST_CONTROL_BUSY:
02388                      if (option_verbose > 2)
02389                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02390                      if (in->cdr)
02391                         ast_cdr_busy(in->cdr);
02392                      do_hang(o);
02393                      endtime = (long)time(NULL);
02394                      endtime -= starttime;
02395                      rna(endtime * 1000, qe, on, membername, 0);
02396                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02397                         if (qe->parent->timeoutrestart)
02398                            *to = orig;
02399                         /* Have enough time for a queue member to answer? */
02400                         if (*to > 500) {
02401                            ring_one(qe, outgoing, &numbusies);
02402                            starttime = (long) time(NULL);
02403                         }
02404                      }
02405                      numbusies++;
02406                      break;
02407                   case AST_CONTROL_CONGESTION:
02408                      if (option_verbose > 2)
02409                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02410                      if (in->cdr)
02411                         ast_cdr_busy(in->cdr);
02412                      endtime = (long)time(NULL);
02413                      endtime -= starttime;
02414                      rna(endtime * 1000, qe, on, membername, 0);
02415                      do_hang(o);
02416                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02417                         if (qe->parent->timeoutrestart)
02418                            *to = orig;
02419                         if (*to > 500) {
02420                            ring_one(qe, outgoing, &numbusies);
02421                            starttime = (long) time(NULL);
02422                         }
02423                      }
02424                      numbusies++;
02425                      break;
02426                   case AST_CONTROL_RINGING:
02427                      if (option_verbose > 2)
02428                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02429 
02430                      /* Start ring indication when the channel is ringing, if specified */
02431                      if (qe->ring_when_ringing) {
02432                         ast_moh_stop(qe->chan);
02433                         ast_indicate(qe->chan, AST_CONTROL_RINGING);
02434                      }
02435                      break;
02436                   case AST_CONTROL_OFFHOOK:
02437                      /* Ignore going off hook */
02438                      break;
02439                   default:
02440                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02441                   }
02442                }
02443                ast_frfree(f);
02444             } else { /* ast_read() returned NULL */
02445                endtime = (long) time(NULL) - starttime;
02446                rna(endtime * 1000, qe, on, membername, 1);
02447                do_hang(o);
02448                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02449                   if (qe->parent->timeoutrestart)
02450                      *to = orig;
02451                   if (*to > 500) {
02452                      ring_one(qe, outgoing, &numbusies);
02453                      starttime = (long) time(NULL);
02454                   }
02455                }
02456             }
02457          }
02458       }
02459 
02460       /* If we received an event from the caller, deal with it. */
02461       if (winner == in) {
02462          f = ast_read(in);
02463          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02464             /* Got hung up */
02465             *to = -1;
02466             if (f)
02467                ast_frfree(f);
02468             return NULL;
02469          }
02470          /* First check if DTMF digit is a valid exit */
02471          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02472             if (option_verbose > 3)
02473                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02474             *to = 0;
02475             *digit = f->subclass;
02476             ast_frfree(f);
02477             return NULL;
02478          }
02479          /* Else check if DTMF should be interpreted as caller disconnect */
02480          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02481             if (option_verbose > 3)
02482                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02483             *to = 0;
02484             ast_frfree(f);
02485             return NULL;
02486          }
02487          ast_frfree(f);
02488       }
02489       if (!*to) {
02490          for (o = start; o; o = o->call_next)
02491             rna(orig, qe, o->interface, o->member->membername, 1);
02492       }
02493    }
02494 
02495    return peer;
02496 }

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

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

Referenced by queue_exec().

02557 {
02558    int res = 0;
02559 
02560    /* This is the holding pen for callers 2 through maxlen */
02561    for (;;) {
02562       enum queue_member_status stat;
02563 
02564       if (is_our_turn(qe))
02565          break;
02566 
02567       /* If we have timed out, break out */
02568       if (qe->expire && (time(NULL) >= qe->expire)) {
02569          *reason = QUEUE_TIMEOUT;
02570          break;
02571       }
02572 
02573       stat = get_member_status(qe->parent, qe->max_penalty);
02574 
02575       /* leave the queue if no agents, if enabled */
02576       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02577          *reason = QUEUE_LEAVEEMPTY;
02578          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02579          leave_queue(qe);
02580          break;
02581       }
02582 
02583       /* leave the queue if no reachable agents, if enabled */
02584       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02585          *reason = QUEUE_LEAVEUNAVAIL;
02586          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02587          leave_queue(qe);
02588          break;
02589       }
02590 
02591       /* Make a position announcement, if enabled */
02592       if (qe->parent->announcefrequency && !ringing &&
02593          (res = say_position(qe)))
02594          break;
02595 
02596       /* If we have timed out, break out */
02597       if (qe->expire && (time(NULL) >= qe->expire)) {
02598          *reason = QUEUE_TIMEOUT;
02599          break;
02600       }
02601 
02602       /* Make a periodic announcement, if enabled */
02603       if (qe->parent->periodicannouncefrequency && !ringing &&
02604          (res = say_periodic_announcement(qe)))
02605          break;
02606 
02607       /* If we have timed out, break out */
02608       if (qe->expire && (time(NULL) >= qe->expire)) {
02609          *reason = QUEUE_TIMEOUT;
02610          break;
02611       }
02612       
02613       /* Wait a second before checking again */
02614       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02615          if (res > 0 && !valid_exit(qe, res))
02616             res = 0;
02617          else
02618             break;
02619       }
02620       
02621       /* If we have timed out, break out */
02622       if (qe->expire && (time(NULL) >= qe->expire)) {
02623          *reason = QUEUE_TIMEOUT;
02624          break;
02625       }
02626    }
02627 
02628    return res;
02629 }


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

char* app = "Queue" [static]

Definition at line 148 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 193 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 195 of file app_queue.c.

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

Definition at line 194 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 228 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 230 of file app_queue.c.

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

Definition at line 229 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 268 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 212 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 214 of file app_queue.c.

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

Definition at line 213 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 251 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 253 of file app_queue.c.

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

Definition at line 252 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5583 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 289 of file app_queue.c.

struct ast_cli_entry cli_add_queue_member_deprecated [static]

Initial value:

 {
   { "add", "queue", "member", NULL },
   handle_queue_add_member, NULL,
   NULL, complete_queue_add_member }

Definition at line 5466 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 5476 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_cli_entry cli_remove_queue_member_deprecated [static]

Initial value:

 {
   { "remove", "queue", "member", NULL },
   handle_queue_remove_member, NULL,
   NULL, complete_queue_remove_member }

Definition at line 5471 of file app_queue.c.

struct ast_cli_entry cli_show_queue_deprecated [static]

Initial value:

 {
   { "show", "queue", NULL },
   queue_show, NULL,
   NULL, complete_queue_show }

Definition at line 5461 of file app_queue.c.

ast_cond_t cond

Condition for the state change queue

Definition at line 734 of file app_queue.c.

char* descrip [static]

Definition at line 152 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

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

struct statechange* first

Definition at line 736 of file app_queue.c.

enum queue_result id

Definition at line 305 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 736 of file app_queue.c.

ast_mutex_t lock

Lock for the state change queue

Definition at line 732 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 292 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 275 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 5455 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 5432 of file app_queue.c.

char qrm_cmd_usage[] [static]

Initial value:

"Usage: queue remove member <channel> from <queue>\n"

Definition at line 5458 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 280 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

struct ast_datastore_info queue_transfer_info [static]

Initial value:

 {
   .type = "queue_transfer",
   .chan_fixup = queue_transfer_fixup,
   .destroy = queue_transfer_destroy,
}
a datastore used to help correctly log attended transfers of queue callers

Definition at line 2725 of file app_queue.c.

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

struct ast_custom_function queueagentcount_function [static]

Definition at line 4511 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4521 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4545 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 4536 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

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

char* text

Definition at line 306 of file app_queue.c.

Referenced by _sip_show_peer(), build_reply_digest(), check_auth(), handle_response(), method_match(), parse_sip_options(), referstatus2str(), reqprep(), sendtext_exec(), set_queue_result(), sip_alloc(), and sip_show_channel().

pthread_t thread

The device state monitoring thread

Definition at line 730 of file app_queue.c.

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 286 of file app_queue.c.


Generated on Tue Apr 6 15:45:38 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7