Fri Feb 19 17:12:51 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 390 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 391 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

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

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 388 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 389 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 392 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 4296 of file app_queue.c.

04296                 {
04297    QMC_VALID = 0, /* Count valid members */
04298    QMC_PAUSED,    /* Count paused members */
04299    QMC_ACTIVE,    /* Count active members */
04300    QMC_FREE,      /* Count free members */
04301    QMC_ALL        /* Count all queue members */
04302 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 568 of file app_queue.c.

00568                          {
00569    QUEUE_NO_MEMBERS,
00570    QUEUE_NO_REACHABLE_MEMBERS,
00571    QUEUE_NORMAL
00572 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 293 of file app_queue.c.

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


Function Documentation

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

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

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

static void __reg_module ( void   )  [static]

Definition at line 5562 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5562 of file app_queue.c.

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

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

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

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

03516 {
03517    struct call_queue *q;
03518    struct member *new_member, *old_member;
03519    int res = RES_NOSUCHQUEUE;
03520 
03521    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03522     * short-circuits if the queue is already in memory. */
03523    if (!(q = load_realtime_queue(queuename)))
03524       return res;
03525 
03526    AST_LIST_LOCK(&queues);
03527 
03528    ao2_lock(q);
03529    if ((old_member = interface_exists(q, interface)) == NULL) {
03530       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03531          add_to_interfaces(new_member->state_interface);
03532          new_member->dynamic = 1;
03533          ao2_link(q->members, new_member);
03534          q->membercount++;
03535          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03536             "Queue: %s\r\n"
03537             "Location: %s\r\n"
03538             "MemberName: %s\r\n"
03539             "Membership: %s\r\n"
03540             "Penalty: %d\r\n"
03541             "CallsTaken: %d\r\n"
03542             "LastCall: %d\r\n"
03543             "Status: %d\r\n"
03544             "Paused: %d\r\n",
03545             q->name, new_member->interface, new_member->membername,
03546             "dynamic",
03547             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03548             new_member->status, new_member->paused);
03549          
03550          ao2_ref(new_member, -1);
03551          new_member = NULL;
03552 
03553          if (dump)
03554             dump_queue_members(q);
03555          
03556          res = RES_OKAY;
03557       } else {
03558          res = RES_OUTOFMEMORY;
03559       }
03560    } else {
03561       ao2_ref(old_member, -1);
03562       res = RES_EXISTS;
03563    }
03564    ao2_unlock(q);
03565    AST_LIST_UNLOCK(&queues);
03566 
03567    return res;
03568 }

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

Definition at line 817 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

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

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

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

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

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

References ast_channel_datastore_find(), and queue_transfer_info.

02755 {
02756    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02757 }

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

02633 {
02634    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02635       return -1;
02636 
02637    switch (q->strategy) {
02638    case QUEUE_STRATEGY_RINGALL:
02639       /* Everyone equal, except for penalty */
02640       tmp->metric = mem->penalty * 1000000;
02641       break;
02642    case QUEUE_STRATEGY_ROUNDROBIN:
02643       if (!pos) {
02644          if (!q->wrapped) {
02645             /* No more channels, start over */
02646             q->rrpos = 0;
02647          } else {
02648             /* Prioritize next entry */
02649             q->rrpos++;
02650          }
02651          q->wrapped = 0;
02652       }
02653       /* Fall through */
02654    case QUEUE_STRATEGY_RRMEMORY:
02655       if (pos < q->rrpos) {
02656          tmp->metric = 1000 + pos;
02657       } else {
02658          if (pos > q->rrpos)
02659             /* Indicate there is another priority */
02660             q->wrapped = 1;
02661          tmp->metric = pos;
02662       }
02663       tmp->metric += mem->penalty * 1000000;
02664       break;
02665    case QUEUE_STRATEGY_RANDOM:
02666       tmp->metric = ast_random() % 1000;
02667       tmp->metric += mem->penalty * 1000000;
02668       break;
02669    case QUEUE_STRATEGY_FEWESTCALLS:
02670       tmp->metric = mem->calls;
02671       tmp->metric += mem->penalty * 1000000;
02672       break;
02673    case QUEUE_STRATEGY_LEASTRECENT:
02674       if (!mem->lastcall)
02675          tmp->metric = 0;
02676       else
02677          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02678       tmp->metric += mem->penalty * 1000000;
02679       break;
02680    default:
02681       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02682       break;
02683    }
02684    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02685       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02686    }
02687    if (option_debug)
02688       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02689                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02690    return 0;
02691 }

static void clear_and_free_interfaces ( void   )  [static]

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

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

static void clear_queue ( struct call_queue q  )  [static]

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

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

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

Definition at line 5389 of file app_queue.c.

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

05390 {
05391    char buffer[256] = "";
05392    char *queuename;
05393    
05394    if (argc != 4) {
05395       return RESULT_SHOWUSAGE;
05396    }
05397    queuename = argv[3];
05398 
05399    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05400          ast_cli(fd, 
05401                      "Member count for queue '%s'\n"
05402                      "%s\n",
05403                      queuename, buffer); 
05404          return RESULT_SUCCESS;
05405    } else {
05406          ast_cli(fd, "No such queue: '%s'\n", queuename);
05407          return RESULT_FAILURE;
05408    }
05409 }

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

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

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

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

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

04892 {
04893    struct call_queue *q;
04894    char *ret = NULL;
04895    int which = 0;
04896    int wordlen = strlen(word);
04897    
04898    AST_LIST_LOCK(&queues);
04899    AST_LIST_TRAVERSE(&queues, q, list) {
04900       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04901          ret = ast_strdup(q->name); 
04902          break;
04903       }
04904    }
04905    AST_LIST_UNLOCK(&queues);
04906 
04907    return ret;
04908 }

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

Definition at line 5194 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05195 {
05196    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05197    switch (pos) {
05198    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05199       return NULL;
05200    case 4:  /* only one possible match, "to" */
05201       return state == 0 ? ast_strdup("to") : NULL;
05202    case 5:  /* <queue> */
05203       return complete_queue(line, word, pos, state);
05204    case 6: /* only one possible match, "penalty" */
05205       return state == 0 ? ast_strdup("penalty") : NULL;
05206    case 7:
05207       if (state < 100) {   /* 0-99 */
05208          char *num;
05209          if ((num = ast_malloc(3))) {
05210             sprintf(num, "%d", state);
05211          }
05212          return num;
05213       } else {
05214          return NULL;
05215       }
05216    case 8: /* only one possible match, "as" */
05217       return state == 0 ? ast_strdup("as") : NULL;
05218    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05219       return NULL;
05220    case 10:
05221       return state == 0 ? ast_strdup("state_interface") : NULL;
05222    default:
05223       return NULL;
05224    }
05225 }

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

Definition at line 5416 of file app_queue.c.

References complete_queue().

05417 {
05418       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05419       switch (pos) {
05420       case 3:  /* <queue> */
05421             return complete_queue(line, word, pos, state);
05422       default:
05423          return NULL;
05424       }
05425 }

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

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

05263 {
05264    int which = 0;
05265    struct call_queue *q;
05266    struct member *m;
05267    struct ao2_iterator mem_iter;
05268 
05269    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05270    if (pos > 5 || pos < 3)
05271       return NULL;
05272    if (pos == 4)  /* only one possible match, 'from' */
05273       return state == 0 ? ast_strdup("from") : NULL;
05274 
05275    if (pos == 5)  /* No need to duplicate code */
05276       return complete_queue(line, word, pos, state);
05277 
05278    /* here is the case for 3, <member> */
05279    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05280       AST_LIST_TRAVERSE(&queues, q, list) {
05281          ao2_lock(q);
05282          mem_iter = ao2_iterator_init(q->members, 0);
05283          while ((m = ao2_iterator_next(&mem_iter))) {
05284             if (++which > state) {
05285                char *tmp;
05286                ao2_iterator_destroy(&mem_iter);
05287                ao2_unlock(q);
05288                tmp = ast_strdup(m->interface);
05289                ao2_ref(m, -1);
05290                return tmp;
05291             }
05292             ao2_ref(m, -1);
05293          }
05294          ao2_iterator_destroy(&mem_iter);
05295          ao2_unlock(q);
05296       }
05297    }
05298 
05299    return NULL;
05300 }

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

Definition at line 4910 of file app_queue.c.

References complete_queue().

04911 {
04912    if (pos == 2)
04913       return complete_queue(line, word, pos, state);
04914    return NULL;
04915 }

static int compress_char ( const char  c  )  [static]

Definition at line 827 of file app_queue.c.

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

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

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

static void destroy_queue ( void *  obj  )  [static]

Definition at line 534 of file app_queue.c.

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

Referenced by alloc_queue().

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

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

Consumer of the statechange queue.

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

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

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1854 of file app_queue.c.

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

Referenced by ring_entry().

01855 {
01856    o->stillgoing = 0;
01857    ast_hangup(o->chan);
01858    o->chan = NULL;
01859 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

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

03424 {
03425    struct member *cur_member;
03426    char value[PM_MAX_LEN];
03427    int value_len = 0;
03428    int res;
03429    struct ao2_iterator mem_iter;
03430 
03431    memset(value, 0, sizeof(value));
03432 
03433    if (!pm_queue)
03434       return;
03435 
03436    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03437    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03438       if (!cur_member->dynamic) {
03439          ao2_ref(cur_member, -1);
03440          continue;
03441       }
03442 
03443       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03444          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03445 
03446       ao2_ref(cur_member, -1);
03447 
03448       if (res != strlen(value + value_len)) {
03449          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03450          break;
03451       }
03452       value_len += res;
03453    }
03454    ao2_iterator_destroy(&mem_iter);
03455    
03456    if (value_len && !cur_member) {
03457       if (ast_db_put(pm_family, pm_queue->name, value))
03458          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03459    } else
03460       /* Delete the entry if the queue is empty or there is an error */
03461       ast_db_del(pm_family, pm_queue->name);
03462 }

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

find the entry with the best metric, or NULL

Definition at line 2061 of file app_queue.c.

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

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

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

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

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

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

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

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

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

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

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

05135 {
05136    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05137    int penalty;
05138 
05139    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05140       return RESULT_SHOWUSAGE;
05141    } else if (strcmp(argv[4], "to")) {
05142       return RESULT_SHOWUSAGE;
05143    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05144       return RESULT_SHOWUSAGE;
05145    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05146       return RESULT_SHOWUSAGE;
05147    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05148       return RESULT_SHOWUSAGE;
05149    }
05150 
05151    queuename = argv[5];
05152    interface = argv[3];
05153    if (argc >= 8) {
05154       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05155          if (penalty < 0) {
05156             ast_cli(fd, "Penalty must be >= 0\n");
05157             penalty = 0;
05158          }
05159       } else {
05160          ast_cli(fd, "Penalty must be an integer >= 0\n");
05161          penalty = 0;
05162       }
05163    } else {
05164       penalty = 0;
05165    }
05166 
05167    if (argc >= 10) {
05168       membername = argv[9];
05169    }
05170 
05171    if (argc >= 12) {
05172       state_interface = argv[11];
05173    }
05174 
05175    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05176    case RES_OKAY:
05177       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05178       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05179       return RESULT_SUCCESS;
05180    case RES_EXISTS:
05181       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05182       return RESULT_FAILURE;
05183    case RES_NOSUCHQUEUE:
05184       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05185       return RESULT_FAILURE;
05186    case RES_OUTOFMEMORY:
05187       ast_cli(fd, "Out of memory\n");
05188       return RESULT_FAILURE;
05189    default:
05190       return RESULT_FAILURE;
05191    }
05192 }

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

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

05228 {
05229    char *queuename, *interface;
05230 
05231    if (argc != 6) {
05232       return RESULT_SHOWUSAGE;
05233    } else if (strcmp(argv[4], "from")) {
05234       return RESULT_SHOWUSAGE;
05235    }
05236 
05237    queuename = argv[5];
05238    interface = argv[3];
05239 
05240    switch (remove_from_queue(queuename, interface)) {
05241    case RES_OKAY:
05242       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05243       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05244       return RESULT_SUCCESS;
05245    case RES_EXISTS:
05246       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05247       return RESULT_FAILURE;
05248    case RES_NOSUCHQUEUE:
05249       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05250       return RESULT_FAILURE;
05251    case RES_OUTOFMEMORY:
05252       ast_cli(fd, "Out of memory\n");
05253       return RESULT_FAILURE;
05254    case RES_NOT_DYNAMIC:
05255       ast_cli(fd, "Member not dynamic\n");
05256       return RESULT_FAILURE;
05257    default:
05258       return RESULT_FAILURE;
05259    }
05260 }

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

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

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

Definition at line 1757 of file app_queue.c.

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

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

static void init_queue ( struct call_queue q  )  [static]

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

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

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

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

Referenced by join_queue().

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

static char* int2strat ( int  strategy  )  [static]

Definition at line 498 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

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

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

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

03397 {
03398    struct member *mem;
03399    struct ao2_iterator mem_iter;
03400 
03401    if (!q)
03402       return NULL;
03403 
03404    mem_iter = ao2_iterator_init(q->members, 0);
03405    while ((mem = ao2_iterator_next(&mem_iter))) {
03406       if (!strcasecmp(interface, mem->interface)) {
03407          ao2_iterator_destroy(&mem_iter);
03408          return mem;
03409       }
03410       ao2_ref(mem, -1);
03411    }
03412    ao2_iterator_destroy(&mem_iter);
03413 
03414    return NULL;
03415 }

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

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

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

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

02492 {
02493    struct queue_ent *ch;
02494    int res;
02495    int avl;
02496    int idx = 0;
02497    /* This needs a lock. How many members are available to be served? */
02498    ao2_lock(qe->parent);
02499 
02500    avl = num_available_members(qe->parent);
02501 
02502    ch = qe->parent->head;
02503 
02504    if (option_debug) {
02505       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02506    }
02507 
02508    while ((idx < avl) && (ch) && (ch != qe)) {
02509       if (!ch->pending)
02510          idx++;
02511       ch = ch->next;       
02512    }
02513 
02514    ao2_unlock(qe->parent);
02515 
02516    /* If the queue entry is within avl [the number of available members] calls from the top ... */
02517    if (ch && idx < avl) {
02518       if (option_debug)
02519          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02520       res = 1;
02521    } else {
02522       if (option_debug)
02523          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02524       res = 0;
02525    }
02526 
02527    return res;
02528 }

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

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

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

static void leave_queue ( struct queue_ent qe  )  [static]

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

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

static int load_module ( void   )  [static]

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

05517 {
05518    int res;
05519 
05520    if (!reload_queues())
05521       return AST_MODULE_LOAD_DECLINE;
05522 
05523    if (queue_persistent_members)
05524       reload_queue_members();
05525 
05526    ast_mutex_init(&device_state.lock);
05527    ast_cond_init(&device_state.cond, NULL);
05528    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05529 
05530    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05531    res = ast_register_application(app, queue_exec, synopsis, descrip);
05532    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05533    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05534    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05535    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05536    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05537    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05538    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05539    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05540    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05541    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05542    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05543    res |= ast_custom_function_register(&queueagentcount_function);
05544    res |= ast_custom_function_register(&queuemembercount_function);
05545    res |= ast_custom_function_register(&queuememberlist_function);
05546    res |= ast_custom_function_register(&queuewaitingcount_function);
05547    res |= ast_devstate_add(statechange_queue, NULL);
05548 
05549    return res;
05550 }

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

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

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

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

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

05027 {
05028    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05029    int paused, penalty = 0;
05030 
05031    queuename = astman_get_header(m, "Queue");
05032    interface = astman_get_header(m, "Interface");
05033    penalty_s = astman_get_header(m, "Penalty");
05034    paused_s = astman_get_header(m, "Paused");
05035    membername = astman_get_header(m, "MemberName");
05036    state_interface = astman_get_header(m, "StateInterface");
05037 
05038    if (ast_strlen_zero(queuename)) {
05039       astman_send_error(s, m, "'Queue' not specified.");
05040       return 0;
05041    }
05042 
05043    if (ast_strlen_zero(interface)) {
05044       astman_send_error(s, m, "'Interface' not specified.");
05045       return 0;
05046    }
05047 
05048    if (ast_strlen_zero(penalty_s))
05049       penalty = 0;
05050    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05051       penalty = 0;
05052 
05053    if (ast_strlen_zero(paused_s))
05054       paused = 0;
05055    else
05056       paused = abs(ast_true(paused_s));
05057 
05058    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05059    case RES_OKAY:
05060       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05061       astman_send_ack(s, m, "Added interface to queue");
05062       break;
05063    case RES_EXISTS:
05064       astman_send_error(s, m, "Unable to add interface: Already there");
05065       break;
05066    case RES_NOSUCHQUEUE:
05067       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05068       break;
05069    case RES_OUTOFMEMORY:
05070       astman_send_error(s, m, "Out of memory");
05071       break;
05072    }
05073 
05074    return 0;
05075 }

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

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

05112 {
05113    const char *queuename, *interface, *paused_s;
05114    int paused;
05115 
05116    interface = astman_get_header(m, "Interface");
05117    paused_s = astman_get_header(m, "Paused");
05118    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05119 
05120    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05121       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05122       return 0;
05123    }
05124 
05125    paused = abs(ast_true(paused_s));
05126 
05127    if (set_member_paused(queuename, interface, paused))
05128       astman_send_error(s, m, "Interface not found");
05129    else
05130       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05131    return 0;
05132 }

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

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

05372 {
05373    char buffer[256] = "";
05374    const char *queuename = astman_get_header(m,"Queue");
05375 
05376    if (ast_strlen_zero(queuename)) {
05377          astman_send_error(s, m, "'Queue' not specified.");
05378          return 0;
05379    }
05380    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05381          astman_send_ack(s, m, buffer);
05382          return RESULT_SUCCESS;
05383    } else {
05384          astman_send_error(s, m, "Queue not found.");
05385          return 0;
05386    }
05387 }

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

Definition at line 4920 of file app_queue.c.

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

Referenced by load_module().

04921 {
04922    char *a[] = { "queue", "show" };
04923 
04924    __queues_show(s, 1, -1, 2, a);
04925    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04926 
04927    return RESULT_SUCCESS;
04928 }

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

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

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

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

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

05078 {
05079    const char *queuename, *interface;
05080 
05081    queuename = astman_get_header(m, "Queue");
05082    interface = astman_get_header(m, "Interface");
05083 
05084    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05085       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05086       return 0;
05087    }
05088 
05089    switch (remove_from_queue(queuename, interface)) {
05090    case RES_OKAY:
05091       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05092       astman_send_ack(s, m, "Removed interface from queue");
05093       break;
05094    case RES_EXISTS:
05095       astman_send_error(s, m, "Unable to remove interface: Not there");
05096       break;
05097    case RES_NOSUCHQUEUE:
05098       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05099       break;
05100    case RES_OUTOFMEMORY:
05101       astman_send_error(s, m, "Out of memory");
05102       break;
05103    case RES_NOT_DYNAMIC:
05104       astman_send_error(s, m, "Member not dynamic");
05105       break;
05106    }
05107 
05108    return 0;
05109 }

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

Definition at line 849 of file app_queue.c.

References member::interface.

Referenced by init_queue().

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

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

Definition at line 837 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

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

static void monjoin_dep_warning ( void   )  [static]

Definition at line 477 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

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

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

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

Referenced by compare_weight(), and is_our_turn().

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

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

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

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

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

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

03714 {
03715    struct ast_module_user *lu;
03716    char *parse;
03717    int priority_jump = 0;
03718    int ignore_fail = 0;
03719    AST_DECLARE_APP_ARGS(args,
03720       AST_APP_ARG(queuename);
03721       AST_APP_ARG(interface);
03722       AST_APP_ARG(options);
03723    );
03724 
03725    if (ast_strlen_zero(data)) {
03726       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03727       return -1;
03728    }
03729 
03730    parse = ast_strdupa(data);
03731 
03732    AST_STANDARD_APP_ARGS(args, parse);
03733 
03734    lu = ast_module_user_add(chan);
03735 
03736    if (args.options) {
03737       if (strchr(args.options, 'j'))
03738          priority_jump = 1;
03739       if (strchr(args.options, 'i'))
03740          ignore_fail = 1;
03741    }
03742 
03743    if (ast_strlen_zero(args.interface)) {
03744       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03745       ast_module_user_remove(lu);
03746       return -1;
03747    }
03748 
03749    if (set_member_paused(args.queuename, args.interface, 1)) {
03750       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03751       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03752       if (priority_jump || ast_opt_priority_jumping) {
03753          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03754             ast_module_user_remove(lu);
03755             return 0;
03756          }
03757       }
03758       ast_module_user_remove(lu);
03759       if (ignore_fail) {
03760          return 0;
03761       } else {
03762          return -1;
03763       }
03764    }
03765 
03766    ast_module_user_remove(lu);
03767    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03768    return 0;
03769 }

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

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

03972 {
03973    struct ast_module_user *u;
03974    char *parse;
03975 
03976    AST_DECLARE_APP_ARGS(args,
03977       AST_APP_ARG(queuename);
03978       AST_APP_ARG(uniqueid);
03979       AST_APP_ARG(membername);
03980       AST_APP_ARG(event);
03981       AST_APP_ARG(params);
03982    );
03983 
03984    if (ast_strlen_zero(data)) {
03985       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03986       return -1;
03987    }
03988 
03989    u = ast_module_user_add(chan);
03990 
03991    parse = ast_strdupa(data);
03992 
03993    AST_STANDARD_APP_ARGS(args, parse);
03994 
03995    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03996        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03997       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03998       ast_module_user_remove(u);
03999       return -1;
04000    }
04001 
04002    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04003       "%s", args.params ? args.params : "");
04004 
04005    ast_module_user_remove(u);
04006 
04007    return 0;
04008 }

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

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

05357 {
05358       struct member_count qmc;
05359       memset(&qmc, 0, sizeof(qmc));
05360       if (queue_member_count(queuename, &qmc) != 0) {
05361             return RESULT_FAILURE; 
05362       } else {
05363             snprintf(buffer, len, 
05364                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05365                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05366             return RESULT_SUCCESS;
05367       }
05368 }

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 4022 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(), 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().

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

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

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

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

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

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

04434 {
04435    struct ast_module_user *u;
04436    struct call_queue *q;
04437    struct member *m;
04438 
04439    /* Ensure an otherwise empty list doesn't return garbage */
04440    buf[0] = '\0';
04441 
04442    if (ast_strlen_zero(data)) {
04443       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04444       return -1;
04445    }
04446    
04447    u = ast_module_user_add(chan);
04448 
04449    AST_LIST_LOCK(&queues);
04450    AST_LIST_TRAVERSE(&queues, q, list) {
04451       if (!strcasecmp(q->name, data)) {
04452          ao2_lock(q);
04453          break;
04454       }
04455    }
04456    AST_LIST_UNLOCK(&queues);
04457 
04458    if (q) {
04459       int buflen = 0, count = 0;
04460       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04461 
04462       while ((m = ao2_iterator_next(&mem_iter))) {
04463          /* strcat() is always faster than printf() */
04464          if (count++) {
04465             strncat(buf + buflen, ",", len - buflen - 1);
04466             buflen++;
04467          }
04468          strncat(buf + buflen, m->interface, len - buflen - 1);
04469          buflen += strlen(m->interface);
04470          /* Safeguard against overflow (negative length) */
04471          if (buflen >= len - 2) {
04472             ao2_ref(m, -1);
04473             ast_log(LOG_WARNING, "Truncating list\n");
04474             break;
04475          }
04476          ao2_ref(m, -1);
04477       }
04478       ao2_iterator_destroy(&mem_iter);
04479       ao2_unlock(q);
04480    } else
04481       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04482 
04483    /* We should already be terminated, but let's make sure. */
04484    buf[len - 1] = '\0';
04485    ast_module_user_remove(u);
04486 
04487    return 0;
04488 }

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

Definition at line 4390 of file app_queue.c.

References ao2_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.

04391 {
04392    int count = 0;
04393    struct call_queue *q;
04394    struct ast_module_user *lu;
04395    struct ast_variable *var = NULL;
04396 
04397    buf[0] = '\0';
04398    
04399    if (ast_strlen_zero(data)) {
04400       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04401       return -1;
04402    }
04403 
04404    lu = ast_module_user_add(chan);
04405    
04406    AST_LIST_LOCK(&queues);
04407    AST_LIST_TRAVERSE(&queues, q, list) {
04408       if (!strcasecmp(q->name, data)) {
04409          ao2_lock(q);
04410          break;
04411       }
04412    }
04413    AST_LIST_UNLOCK(&queues);
04414 
04415    if (q) {
04416       count = q->count;
04417       ao2_unlock(q);
04418    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04419       /* if the queue is realtime but was not found in memory, this
04420        * means that the queue had been deleted from memory since it was 
04421        * "dead." This means it has a 0 waiting count
04422        */
04423       count = 0;
04424       ast_variables_destroy(var);
04425    } else
04426       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04427 
04428    snprintf(buf, len, "%d", count);
04429    ast_module_user_remove(lu);
04430    return 0;
04431 }

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

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

05315 {
05316       int res = 0;
05317       struct call_queue *q;
05318       struct member *m;
05319       struct ao2_iterator mem_iter;
05320       
05321       if ((q = load_realtime_queue(qname))) {
05322             ao2_lock(q);
05323             mem_iter = ao2_iterator_init(q->members, 0);
05324             while ((m = ao2_iterator_next(&mem_iter))) {
05325                   /* Count the queue members in use */
05326                   if (m->status == AST_DEVICE_INUSE) {
05327                         qmc->inuse++;
05328                   }
05329                   /* Count the queue members who are logged in and presently answering calls */
05330                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05331                         qmc->valid++;
05332                   }
05333                   /* Count paused members */
05334                   if (m->paused) {
05335                         qmc->paused++;
05336                   }
05337                   /* Count not paused members who are logged in and presently answering calls */
05338                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05339                         qmc->active++;
05340                   }
05341                   /* Count free members in the queue */
05342                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05343                         qmc->free++;
05344                   }
05345                   qmc->all++;
05346                   ao2_ref(m, -1);
05347             }
05348             ao2_unlock(q);
05349       } else {
05350             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05351             res = -1;
05352       }
05353       return res;
05354 }

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

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

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

Definition at line 4886 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04887 {
04888    return __queues_show(NULL, 0, fd, argc, argv);
04889 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2700 of file app_queue.c.

References ast_free.

02701 {
02702    struct queue_transfer_ds *qtds = data;
02703    ast_free(qtds);
02704 }

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

02724 {
02725    struct queue_transfer_ds *qtds = data;
02726    struct queue_ent *qe = qtds->qe;
02727    struct member *member = qtds->member;
02728    time_t callstart = qtds->starttime;
02729    int callcompletedinsl = qtds->callcompletedinsl;
02730    struct ast_datastore *datastore;
02731 
02732    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02733             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02734             (long) (time(NULL) - callstart));
02735 
02736    update_queue(qe->parent, member, callcompletedinsl);
02737    
02738    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02739    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02740       ast_channel_datastore_remove(old_chan, datastore);
02741    } else {
02742       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02743    }
02744 }

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

Definition at line 1701 of file app_queue.c.

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

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

static void record_abandoned ( struct queue_ent qe  )  [static]

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

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

static int reload ( void   )  [static]

Definition at line 5552 of file app_queue.c.

References reload_queues().

05553 {
05554    reload_queues();
05555    return 0;
05556 }

static void reload_queue_members ( void   )  [static]

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

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

static int reload_queues ( void   )  [static]

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

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

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

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

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

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

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

03465 {
03466    struct call_queue *q;
03467    struct member *mem, tmpmem;
03468    int res = RES_NOSUCHQUEUE;
03469 
03470    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03471 
03472    AST_LIST_LOCK(&queues);
03473    AST_LIST_TRAVERSE(&queues, q, list) {
03474       ao2_lock(q);
03475       if (strcmp(q->name, queuename)) {
03476          ao2_unlock(q);
03477          continue;
03478       }
03479 
03480       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03481          /* XXX future changes should beware of this assumption!! */
03482          if (!mem->dynamic) {
03483             res = RES_NOT_DYNAMIC;
03484             ao2_ref(mem, -1);
03485             ao2_unlock(q);
03486             break;
03487          }
03488          q->membercount--;
03489          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03490             "Queue: %s\r\n"
03491             "Location: %s\r\n"
03492             "MemberName: %s\r\n",
03493             q->name, mem->interface, mem->membername);
03494          ao2_unlink(q->members, mem);
03495          remove_from_interfaces(mem->state_interface);
03496          ao2_ref(mem, -1);
03497 
03498          if (queue_persistent_members)
03499             dump_queue_members(q);
03500          
03501          res = RES_OKAY;
03502       } else {
03503          res = RES_EXISTS;
03504       }
03505       ao2_unlock(q);
03506       break;
03507    }
03508 
03509    AST_LIST_UNLOCK(&queues);
03510 
03511    return res;
03512 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

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

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

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

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

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

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

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

References ast_queue_log(), ast_verbose(), call_queue::autopause, queue_ent::chan, call_queue::name, option_verbose, queue_ent::parent, set_member_paused(), ast_channel::uniqueid, and VERBOSE_PREFIX_3.

02199 {
02200    if (option_verbose > 2)
02201       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02202    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02203    if (qe->parent->autopause && pause) {
02204       if (!set_member_paused(qe->parent->name, interface, 1)) {
02205          if (option_verbose > 2)
02206             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02207       } else {
02208          if (option_verbose > 2)
02209             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02210       }
02211    }
02212    return;
02213 }

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

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

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

static void rr_dep_warning ( void   )  [static]

Definition at line 467 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

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

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

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

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

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

static int say_position ( struct queue_ent qe  )  [static]

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

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

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

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

03571 {
03572    int found = 0;
03573    struct call_queue *q;
03574    struct member *mem;
03575 
03576    /* Special event for when all queues are paused - individual events still generated */
03577    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03578    if (ast_strlen_zero(queuename))
03579       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03580 
03581    AST_LIST_LOCK(&queues);
03582    AST_LIST_TRAVERSE(&queues, q, list) {
03583       ao2_lock(q);
03584       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03585          if ((mem = interface_exists(q, interface))) {
03586             found++;
03587             if (mem->paused == paused)
03588                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03589             mem->paused = paused;
03590 
03591             if (queue_persistent_members)
03592                dump_queue_members(q);
03593 
03594             if (mem->realtime)
03595                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03596 
03597             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03598 
03599             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03600                "Queue: %s\r\n"
03601                "Location: %s\r\n"
03602                "MemberName: %s\r\n"
03603                "Paused: %d\r\n",
03604                   q->name, mem->interface, mem->membername, paused);
03605             ao2_ref(mem, -1);
03606          }
03607       }
03608       ao2_unlock(q);
03609    }
03610    AST_LIST_UNLOCK(&queues);
03611 
03612    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03613 }

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

sets the QUEUESTATUS channel variable

Definition at line 486 of file app_queue.c.

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

Referenced by queue_exec().

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

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 2761 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.

02762 {
02763    struct ast_datastore *ds;
02764    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02765 
02766    if (!qtds) {
02767       ast_log(LOG_WARNING, "Memory allocation error!\n");
02768       return NULL;
02769    }
02770 
02771    ast_channel_lock(qe->chan);
02772    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02773       ast_channel_unlock(qe->chan);
02774       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02775       return NULL;
02776    }
02777 
02778    qtds->qe = qe;
02779    /* This member is refcounted in try_calling, so no need to add it here, too */
02780    qtds->member = member;
02781    qtds->starttime = starttime;
02782    qtds->callcompletedinsl = callcompletedinsl;
02783    ds->data = qtds;
02784    ast_channel_datastore_add(qe->chan, ds);
02785    ast_channel_unlock(qe->chan);
02786    return ds;
02787 }

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

Producer of the statechange queue.

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

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

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

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

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

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

Definition at line 510 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

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

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

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

static int unload_module ( void   )  [static]

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

05480 {
05481    int res;
05482 
05483    if (device_state.thread != AST_PTHREADT_NULL) {
05484       device_state.stop = 1;
05485       ast_mutex_lock(&device_state.lock);
05486       ast_cond_signal(&device_state.cond);
05487       ast_mutex_unlock(&device_state.lock);
05488       pthread_join(device_state.thread, NULL);
05489    }
05490 
05491    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05492    res = ast_manager_unregister("QueueStatus");
05493    res |= ast_manager_unregister("Queues");
05494    res |= ast_manager_unregister("QueueAdd");
05495    res |= ast_manager_unregister("QueueRemove");
05496    res |= ast_manager_unregister("QueuePause");
05497    res |= ast_unregister_application(app_aqm);
05498    res |= ast_unregister_application(app_rqm);
05499    res |= ast_unregister_application(app_pqm);
05500    res |= ast_unregister_application(app_upqm);
05501    res |= ast_unregister_application(app_ql);
05502    res |= ast_unregister_application(app);
05503    res |= ast_custom_function_unregister(&queueagentcount_function);
05504    res |= ast_custom_function_unregister(&queuemembercount_function);
05505    res |= ast_custom_function_unregister(&queuememberlist_function);
05506    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05507    ast_devstate_del(statechange_queue, NULL);
05508 
05509    ast_module_user_hangup_all();
05510 
05511    clear_and_free_interfaces();
05512 
05513    return res;
05514 }

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

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

02615 {
02616    ao2_lock(q);
02617    time(&member->lastcall);
02618    member->calls++;
02619    q->callscompleted++;
02620    if (callcompletedinsl)
02621       q->callscompletedinsl++;
02622    ao2_unlock(q);
02623    return 0;
02624 }

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

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

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

static void update_realtime_members ( struct call_queue q  )  [static]

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

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

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

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

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

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

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

03772 {
03773    struct ast_module_user *lu;
03774    char *parse;
03775    int priority_jump = 0;
03776    int ignore_fail = 0;
03777    AST_DECLARE_APP_ARGS(args,
03778       AST_APP_ARG(queuename);
03779       AST_APP_ARG(interface);
03780       AST_APP_ARG(options);
03781    );
03782 
03783    if (ast_strlen_zero(data)) {
03784       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03785       return -1;
03786    }
03787 
03788    parse = ast_strdupa(data);
03789 
03790    AST_STANDARD_APP_ARGS(args, parse);
03791 
03792    lu = ast_module_user_add(chan);
03793 
03794    if (args.options) {
03795       if (strchr(args.options, 'j'))
03796          priority_jump = 1;
03797       if (strchr(args.options, 'i'))
03798          ignore_fail = 1;
03799    }
03800 
03801    if (ast_strlen_zero(args.interface)) {
03802       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03803       ast_module_user_remove(lu);
03804       return -1;
03805    }
03806 
03807    if (set_member_paused(args.queuename, args.interface, 0)) {
03808       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03809       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03810       if (priority_jump || ast_opt_priority_jumping) {
03811          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03812             ast_module_user_remove(lu);
03813             return 0;
03814          }
03815       }
03816       ast_module_user_remove(lu);
03817       if (ignore_fail) {
03818          return 0;
03819       } else {
03820          return -1;
03821       }
03822    }
03823 
03824    ast_module_user_remove(lu);
03825    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03826    return 0;
03827 }

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

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

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

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

Definition at line 1861 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry().

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

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3384 of file app_queue.c.

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

Referenced by queue_exec().

03385 {
03386    /* Don't need to hold the lock while we setup the outgoing calls */
03387    int retrywait = qe->parent->retry * 1000;
03388 
03389    int res = ast_waitfordigit(qe->chan, retrywait);
03390    if (res > 0 && !valid_exit(qe, res))
03391       res = 0;
03392 
03393    return res;
03394 }

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 2226 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.

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

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

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


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

char* app_aqm_descrip [static]

Definition at line 194 of file app_queue.c.

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

Definition at line 193 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 227 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 229 of file app_queue.c.

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

Definition at line 228 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 267 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 211 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 213 of file app_queue.c.

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

Definition at line 212 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 250 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 252 of file app_queue.c.

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

Definition at line 251 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5562 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

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

struct ast_cli_entry cli_queue[] [static]

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

ast_cond_t cond

Condition for the state change queue

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

enum queue_result id

Definition at line 304 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 734 of file app_queue.c.

ast_mutex_t lock

Lock for the state change queue

Definition at line 730 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 291 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 274 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 5434 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 5411 of file app_queue.c.

char qrm_cmd_usage[] [static]

Initial value:

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

Definition at line 5437 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 279 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 282 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 5430 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 2708 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 4490 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4500 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4524 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

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

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 285 of file app_queue.c.


Generated on Fri Feb 19 17:12:51 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7