Sat Aug 6 00:39:35 2011

Asterisk developer's documentation


app_queue.c File Reference

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

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

Go to the source code of this file.

Data Structures

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

Defines

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

Enumerations

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

Functions

static int __queues_show (struct mansession *s, int manager, int fd, int argc, char **argv)
static void __reg_module (void)
static void __unreg_module (void)
static int add_to_interfaces (const char *interface)
static int add_to_queue (const char *queuename, const char *interface, const char *membername, int penalty, int paused, int dump, const char *state_interface)
static struct call_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 394 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 395 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2268 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 139 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 140 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 142 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 279 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 392 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

Definition at line 393 of file app_queue.c.

Referenced by join_queue(), queue_exec(), queue_set_param(), and wait_our_turn().

#define QUEUE_EVENT_VARIABLES   3

Definition at line 396 of file app_queue.c.

Referenced by queue_set_param(), and ring_entry().

#define RECHECK   1

Definition at line 141 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Definition at line 145 of file app_queue.c.

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

#define RES_NOSUCHQUEUE   (-3)

Definition at line 147 of file app_queue.c.

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

#define RES_NOT_DYNAMIC   (-4)

Definition at line 148 of file app_queue.c.

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

#define RES_OKAY   0

Definition at line 144 of file app_queue.c.

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

#define RES_OUTOFMEMORY   (-2)

Definition at line 146 of file app_queue.c.

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


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 
QUEUE_STRATEGY_RRORDERED 

Definition at line 116 of file app_queue.c.

enum qmc_status

Enumerator:
QMC_VALID 
QMC_PAUSED 
QMC_ACTIVE 
QMC_FREE 
QMC_ALL 

Definition at line 4388 of file app_queue.c.

04388                 {
04389    QMC_VALID = 0, /* Count valid members */
04390    QMC_PAUSED,    /* Count paused members */
04391    QMC_ACTIVE,    /* Count active members */
04392    QMC_FREE,      /* Count free members */
04393    QMC_ALL        /* Count all queue members */
04394 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 572 of file app_queue.c.

00572                          {
00573    QUEUE_NO_MEMBERS,
00574    QUEUE_NO_REACHABLE_MEMBERS,
00575    QUEUE_NORMAL
00576 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 296 of file app_queue.c.

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


Function Documentation

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

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

04844 {
04845    struct call_queue *q;
04846    struct queue_ent *qe;
04847    struct member *mem;
04848    int pos, queue_show;
04849    time_t now;
04850    char max_buf[150];
04851    char *max;
04852    size_t max_left;
04853    float sl = 0;
04854    char *term = manager ? "\r\n" : "\n";
04855    struct ao2_iterator mem_iter;
04856 
04857    time(&now);
04858    if (argc == 2)
04859       queue_show = 0;
04860    else if (argc == 3)
04861       queue_show = 1;
04862    else
04863       return RESULT_SHOWUSAGE;
04864 
04865    /* We only want to load realtime queues when a specific queue is asked for. */
04866    if (queue_show) {
04867       load_realtime_queue(argv[2]);
04868    } else if (ast_check_realtime("queues")) {
04869       struct ast_config *cfg = ast_load_realtime_multientry("queues", "name LIKE", "%", (char *) NULL);
04870       char *queuename;
04871       if (cfg) {
04872          for (queuename = ast_category_browse(cfg, NULL); !ast_strlen_zero(queuename); queuename = ast_category_browse(cfg, queuename)) {
04873             load_realtime_queue(queuename);
04874          }
04875          ast_config_destroy(cfg);
04876       }
04877    }
04878 
04879    AST_LIST_LOCK(&queues);
04880    if (AST_LIST_EMPTY(&queues)) {
04881       AST_LIST_UNLOCK(&queues);
04882       if (queue_show) {
04883          if (s)
04884             astman_append(s, "No such queue: %s.%s",argv[2], term);
04885          else
04886             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04887       } else {
04888          if (s)
04889             astman_append(s, "No queues.%s", term);
04890          else
04891             ast_cli(fd, "No queues.%s", term);
04892       }
04893       return RESULT_SUCCESS;
04894    }
04895    AST_LIST_TRAVERSE(&queues, q, list) {
04896       ao2_lock(q);
04897       if (queue_show) {
04898          if (strcasecmp(q->name, argv[2]) != 0) {
04899             ao2_unlock(q);
04900             if (!AST_LIST_NEXT(q, list)) {
04901                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04902                break;
04903             }
04904             continue;
04905          }
04906       }
04907       max_buf[0] = '\0';
04908       max = max_buf;
04909       max_left = sizeof(max_buf);
04910       if (q->maxlen)
04911          ast_build_string(&max, &max_left, "%d", q->maxlen);
04912       else
04913          ast_build_string(&max, &max_left, "unlimited");
04914       sl = 0;
04915       if (q->callscompleted > 0)
04916          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04917       if (s)
04918          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",
04919                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04920                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04921       else
04922          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",
04923                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04924                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04925       if (ao2_container_count(q->members)) {
04926          if (s)
04927             astman_append(s, "   Members: %s", term);
04928          else
04929             ast_cli(fd, "   Members: %s", term);
04930          mem_iter = ao2_iterator_init(q->members, 0);
04931          while ((mem = ao2_iterator_next(&mem_iter))) {
04932             max_buf[0] = '\0';
04933             max = max_buf;
04934             max_left = sizeof(max_buf);
04935             if (strcasecmp(mem->membername, mem->interface)) {
04936                ast_build_string(&max, &max_left, " (%s)", mem->interface);
04937             }
04938             if (mem->penalty)
04939                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04940             if (mem->dynamic)
04941                ast_build_string(&max, &max_left, " (dynamic)");
04942             if (mem->realtime)
04943                ast_build_string(&max, &max_left, " (realtime)");
04944             if (mem->paused)
04945                ast_build_string(&max, &max_left, " (paused)");
04946             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04947             if (mem->calls) {
04948                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04949                   mem->calls, (long) (time(NULL) - mem->lastcall));
04950             } else
04951                ast_build_string(&max, &max_left, " has taken no calls yet");
04952             if (s)
04953                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04954             else
04955                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04956             ao2_ref(mem, -1);
04957          }
04958          ao2_iterator_destroy(&mem_iter);
04959       } else if (s)
04960          astman_append(s, "   No Members%s", term);
04961       else  
04962          ast_cli(fd, "   No Members%s", term);
04963       if (q->head) {
04964          pos = 1;
04965          if (s)
04966             astman_append(s, "   Callers: %s", term);
04967          else
04968             ast_cli(fd, "   Callers: %s", term);
04969          for (qe = q->head; qe; qe = qe->next) {
04970             if (s)
04971                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04972                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04973                   (long) (now - qe->start) % 60, qe->prio, term);
04974             else
04975                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04976                   qe->chan->name, (long) (now - qe->start) / 60,
04977                   (long) (now - qe->start) % 60, qe->prio, term);
04978          }
04979       } else if (s)
04980          astman_append(s, "   No Callers%s", term);
04981       else
04982          ast_cli(fd, "   No Callers%s", term);
04983       if (s)
04984          astman_append(s, "%s", term);
04985       else
04986          ast_cli(fd, "%s", term);
04987       ao2_unlock(q);
04988       if (queue_show)
04989          break;
04990    }
04991    AST_LIST_UNLOCK(&queues);
04992    return RESULT_SUCCESS;
04993 }

static void __reg_module ( void   )  [static]

Definition at line 5671 of file app_queue.c.

static void __unreg_module ( void   )  [static]

Definition at line 5671 of file app_queue.c.

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

Definition at line 933 of file app_queue.c.

References ast_calloc, ast_copy_string(), AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), member_interface::interface, member_interface::list, LOG_DEBUG, and option_debug.

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

00934 {
00935    struct member_interface *curint;
00936 
00937    AST_LIST_LOCK(&interfaces);
00938    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00939       if (!strcasecmp(curint->interface, interface))
00940          break;
00941    }
00942 
00943    if (curint) {
00944       AST_LIST_UNLOCK(&interfaces);
00945       return 0;
00946    }
00947 
00948    if (option_debug)
00949       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00950    
00951    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00952       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00953       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00954    }
00955    AST_LIST_UNLOCK(&interfaces);
00956 
00957    return 0;
00958 }

static int add_to_queue ( const char *  queuename,
const char *  interface,
const char *  membername,
int  penalty,
int  paused,
int  dump,
const char *  state_interface 
) [static]

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

03612 {
03613    struct call_queue *q;
03614    struct member *new_member, *old_member;
03615    int res = RES_NOSUCHQUEUE;
03616 
03617    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03618     * short-circuits if the queue is already in memory. */
03619    if (!(q = load_realtime_queue(queuename)))
03620       return res;
03621 
03622    AST_LIST_LOCK(&queues);
03623 
03624    ao2_lock(q);
03625    if ((old_member = interface_exists(q, interface)) == NULL) {
03626       if ((new_member = create_queue_member(interface, membername, penalty, paused, state_interface))) {
03627          add_to_interfaces(new_member->state_interface);
03628          new_member->dynamic = 1;
03629          ao2_link(q->members, new_member);
03630          q->membercount++;
03631          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03632             "Queue: %s\r\n"
03633             "Location: %s\r\n"
03634             "MemberName: %s\r\n"
03635             "Membership: %s\r\n"
03636             "Penalty: %d\r\n"
03637             "CallsTaken: %d\r\n"
03638             "LastCall: %d\r\n"
03639             "Status: %d\r\n"
03640             "Paused: %d\r\n",
03641             q->name, new_member->interface, new_member->membername,
03642             "dynamic",
03643             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03644             new_member->status, new_member->paused);
03645          
03646          ao2_ref(new_member, -1);
03647          new_member = NULL;
03648 
03649          if (dump)
03650             dump_queue_members(q);
03651          
03652          res = RES_OKAY;
03653       } else {
03654          res = RES_OUTOFMEMORY;
03655       }
03656    } else {
03657       ao2_ref(old_member, -1);
03658       res = RES_EXISTS;
03659    }
03660    ao2_unlock(q);
03661    AST_LIST_UNLOCK(&queues);
03662 
03663    return res;
03664 }

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

Definition at line 830 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

00831 {
00832    struct call_queue *q;
00833 
00834    if ((q = ao2_alloc(sizeof(*q), destroy_queue))) {
00835       ast_copy_string(q->name, queuename, sizeof(q->name));
00836    }
00837    return q;
00838 }

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

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

03993 {
03994    int res=-1;
03995    struct ast_module_user *lu;
03996    char *parse, *temppos = NULL;
03997    int priority_jump = 0;
03998    AST_DECLARE_APP_ARGS(args,
03999       AST_APP_ARG(queuename);
04000       AST_APP_ARG(interface);
04001       AST_APP_ARG(penalty);
04002       AST_APP_ARG(options);
04003       AST_APP_ARG(membername);
04004       AST_APP_ARG(state_interface);
04005    );
04006    int penalty = 0;
04007 
04008    if (ast_strlen_zero(data)) {
04009       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|interface[|penalty[|options[|membername[|state_interface]]]]])\n");
04010       return -1;
04011    }
04012 
04013    parse = ast_strdupa(data);
04014 
04015    AST_STANDARD_APP_ARGS(args, parse);
04016 
04017    lu = ast_module_user_add(chan);
04018 
04019    if (ast_strlen_zero(args.interface)) {
04020       args.interface = ast_strdupa(chan->name);
04021       temppos = strrchr(args.interface, '-');
04022       if (temppos)
04023          *temppos = '\0';
04024    }
04025 
04026    if (!ast_strlen_zero(args.penalty)) {
04027       if ((sscanf(args.penalty, "%30d", &penalty) != 1) || penalty < 0) {
04028          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
04029          penalty = 0;
04030       }
04031    }
04032    
04033    if (args.options) {
04034       if (strchr(args.options, 'j'))
04035          priority_jump = 1;
04036    }
04037 
04038    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members, args.state_interface)) {
04039    case RES_OKAY:
04040       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
04041       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
04042       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
04043       res = 0;
04044       break;
04045    case RES_EXISTS:
04046       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
04047       if (priority_jump || ast_opt_priority_jumping)
04048          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
04049       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
04050       res = 0;
04051       break;
04052    case RES_NOSUCHQUEUE:
04053       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
04054       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
04055       res = 0;
04056       break;
04057    case RES_OUTOFMEMORY:
04058       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
04059       break;
04060    }
04061 
04062    ast_module_user_remove(lu);
04063 
04064    return res;
04065 }

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

References ast_channel_datastore_find(), and queue_transfer_info.

02820 {
02821    return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
02822 }

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

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

Referenced by try_calling().

02697 {
02698    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02699       return -1;
02700 
02701    switch (q->strategy) {
02702    case QUEUE_STRATEGY_RINGALL:
02703       /* Everyone equal, except for penalty */
02704       tmp->metric = mem->penalty * 1000000;
02705       break;
02706    case QUEUE_STRATEGY_ROUNDROBIN:
02707       if (!pos) {
02708          if (!q->wrapped) {
02709             /* No more channels, start over */
02710             q->rrpos = 0;
02711          } else {
02712             /* Prioritize next entry */
02713             q->rrpos++;
02714          }
02715          q->wrapped = 0;
02716       }
02717       /* Fall through */
02718    case QUEUE_STRATEGY_RRORDERED:
02719    case QUEUE_STRATEGY_RRMEMORY:
02720       if (pos < q->rrpos) {
02721          tmp->metric = 1000 + pos;
02722       } else {
02723          if (pos > q->rrpos)
02724             /* Indicate there is another priority */
02725             q->wrapped = 1;
02726          tmp->metric = pos;
02727       }
02728       tmp->metric += mem->penalty * 1000000;
02729       break;
02730    case QUEUE_STRATEGY_RANDOM:
02731       tmp->metric = ast_random() % 1000;
02732       tmp->metric += mem->penalty * 1000000;
02733       break;
02734    case QUEUE_STRATEGY_FEWESTCALLS:
02735       tmp->metric = mem->calls;
02736       tmp->metric += mem->penalty * 1000000;
02737       break;
02738    case QUEUE_STRATEGY_LEASTRECENT:
02739       if (!mem->lastcall)
02740          tmp->metric = 0;
02741       else
02742          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02743       tmp->metric += mem->penalty * 1000000;
02744       break;
02745    default:
02746       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02747       break;
02748    }
02749    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02750       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02751    }
02752    if (option_debug)
02753       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02754                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02755    return 0;
02756 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 1012 of file app_queue.c.

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

Referenced by unload_module().

01013 {
01014    struct member_interface *curint;
01015 
01016    AST_LIST_LOCK(&interfaces);
01017    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
01018       free(curint);
01019    AST_LIST_UNLOCK(&interfaces);
01020 }

static void clear_queue ( struct call_queue q  )  [static]

Definition at line 924 of file app_queue.c.

References call_queue::callsabandoned, call_queue::callscompleted, call_queue::callscompletedinsl, call_queue::holdtime, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00925 {
00926    q->holdtime = 0;
00927    q->callscompleted = 0;
00928    q->callsabandoned = 0;
00929    q->callscompletedinsl = 0;
00930    q->wrapuptime = 0;
00931 }

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

Definition at line 5498 of file app_queue.c.

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

05499 {
05500    char buffer[256] = "";
05501    char *queuename;
05502    
05503    if (argc != 4) {
05504       return RESULT_SHOWUSAGE;
05505    }
05506    queuename = argv[3];
05507 
05508    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05509          ast_cli(fd, 
05510                      "Member count for queue '%s'\n"
05511                      "%s\n",
05512                      queuename, buffer); 
05513          return RESULT_SUCCESS;
05514    } else {
05515          ast_cli(fd, "No such queue: '%s'\n", queuename);
05516          return RESULT_FAILURE;
05517    }
05518 }

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

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

01871 {
01872    struct call_queue *q;
01873    struct member *mem;
01874    int found = 0;
01875    
01876    /* &qlock and &rq->lock already set by try_calling()
01877     * to solve deadlock */
01878    AST_LIST_TRAVERSE(&queues, q, list) {
01879       if (q == rq) /* don't check myself, could deadlock */
01880          continue;
01881       ao2_lock(q);
01882       if (q->count && q->members) {
01883          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01884             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01885             if (q->weight > rq->weight && q->count >= num_available_members(q)) {
01886                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);
01887                found = 1;
01888             }
01889             ao2_ref(mem, -1);
01890          }
01891       }
01892       ao2_unlock(q);
01893       if (found)
01894          break;
01895    }
01896    return found;
01897 }

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

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

05001 {
05002    struct call_queue *q;
05003    char *ret = NULL;
05004    int which = 0;
05005    int wordlen = strlen(word);
05006    
05007    AST_LIST_LOCK(&queues);
05008    AST_LIST_TRAVERSE(&queues, q, list) {
05009       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
05010          ret = ast_strdup(q->name); 
05011          break;
05012       }
05013    }
05014    AST_LIST_UNLOCK(&queues);
05015 
05016    return ret;
05017 }

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

Definition at line 5303 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

05304 {
05305    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
05306    switch (pos) {
05307    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
05308       return NULL;
05309    case 4:  /* only one possible match, "to" */
05310       return state == 0 ? ast_strdup("to") : NULL;
05311    case 5:  /* <queue> */
05312       return complete_queue(line, word, pos, state);
05313    case 6: /* only one possible match, "penalty" */
05314       return state == 0 ? ast_strdup("penalty") : NULL;
05315    case 7:
05316       if (state < 100) {   /* 0-99 */
05317          char *num;
05318          if ((num = ast_malloc(3))) {
05319             sprintf(num, "%d", state);
05320          }
05321          return num;
05322       } else {
05323          return NULL;
05324       }
05325    case 8: /* only one possible match, "as" */
05326       return state == 0 ? ast_strdup("as") : NULL;
05327    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
05328       return NULL;
05329    case 10:
05330       return state == 0 ? ast_strdup("state_interface") : NULL;
05331    default:
05332       return NULL;
05333    }
05334 }

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

Definition at line 5525 of file app_queue.c.

References complete_queue().

05526 {
05527       /* 0 - queue; 1 - member; 2 - count; 3 - <queue> */
05528       switch (pos) {
05529       case 3:  /* <queue> */
05530             return complete_queue(line, word, pos, state);
05531       default:
05532          return NULL;
05533       }
05534 }

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

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

05372 {
05373    int which = 0;
05374    struct call_queue *q;
05375    struct member *m;
05376    struct ao2_iterator mem_iter;
05377 
05378    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
05379    if (pos > 5 || pos < 3)
05380       return NULL;
05381    if (pos == 4)  /* only one possible match, 'from' */
05382       return state == 0 ? ast_strdup("from") : NULL;
05383 
05384    if (pos == 5)  /* No need to duplicate code */
05385       return complete_queue(line, word, pos, state);
05386 
05387    /* here is the case for 3, <member> */
05388    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
05389       AST_LIST_TRAVERSE(&queues, q, list) {
05390          ao2_lock(q);
05391          mem_iter = ao2_iterator_init(q->members, 0);
05392          while ((m = ao2_iterator_next(&mem_iter))) {
05393             if (++which > state) {
05394                char *tmp;
05395                ao2_iterator_destroy(&mem_iter);
05396                ao2_unlock(q);
05397                tmp = ast_strdup(m->interface);
05398                ao2_ref(m, -1);
05399                return tmp;
05400             }
05401             ao2_ref(m, -1);
05402          }
05403          ao2_iterator_destroy(&mem_iter);
05404          ao2_unlock(q);
05405       }
05406    }
05407 
05408    return NULL;
05409 }

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

Definition at line 5019 of file app_queue.c.

References complete_queue().

05020 {
05021    if (pos == 2)
05022       return complete_queue(line, word, pos, state);
05023    return NULL;
05024 }

static int compress_char ( const char  c  )  [static]

Definition at line 840 of file app_queue.c.

00841 {
00842    if (c < 32)
00843       return 0;
00844    else if (c > 96)
00845       return c - 64;
00846    else
00847       return c - 32;
00848 }

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

00806 {
00807    struct member *cur;
00808    
00809    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00810       cur->penalty = penalty;
00811       cur->paused = paused;
00812       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00813       if (!ast_strlen_zero(state_interface)) {
00814          ast_copy_string(cur->state_interface, state_interface, sizeof(cur->state_interface));
00815       } else {
00816          ast_copy_string(cur->state_interface, interface, sizeof(cur->state_interface));
00817       }
00818       if (!ast_strlen_zero(membername))
00819          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00820       else
00821          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00822       if (!strchr(cur->interface, '/'))
00823          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00824       cur->status = ast_device_state(cur->state_interface);
00825    }
00826 
00827    return cur;
00828 }

static void destroy_queue ( void *  obj  )  [static]

Definition at line 538 of file app_queue.c.

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

Referenced by alloc_queue().

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

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

Consumer of the statechange queue.

Definition at line 753 of file app_queue.c.

References ast_cond_wait(), AST_LIST_REMOVE_HEAD, ast_mutex_lock(), ast_mutex_unlock(), device_state, statechange::entry, free, and handle_statechange().

Referenced by load_module().

00754 {
00755    struct statechange *sc = NULL;
00756 
00757    while (!device_state.stop) {
00758       ast_mutex_lock(&device_state.lock);
00759       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00760          ast_cond_wait(&device_state.cond, &device_state.lock);
00761          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00762       }
00763       ast_mutex_unlock(&device_state.lock);
00764 
00765       /* Check to see if we were woken up to see the request to stop */
00766       if (device_state.stop)
00767          break;
00768 
00769       if (!sc)
00770          continue;
00771 
00772       handle_statechange(sc);
00773 
00774       free(sc);
00775       sc = NULL;
00776    }
00777 
00778    if (sc)
00779       free(sc);
00780 
00781    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00782       free(sc);
00783 
00784    return NULL;
00785 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1900 of file app_queue.c.

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

Referenced by ring_entry().

01901 {
01902    o->stillgoing = 0;
01903    ast_hangup(o->chan);
01904    o->chan = NULL;
01905 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

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

03520 {
03521    struct member *cur_member;
03522    char value[PM_MAX_LEN];
03523    int value_len = 0;
03524    int res;
03525    struct ao2_iterator mem_iter;
03526 
03527    memset(value, 0, sizeof(value));
03528 
03529    if (!pm_queue)
03530       return;
03531 
03532    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03533    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03534       if (!cur_member->dynamic) {
03535          ao2_ref(cur_member, -1);
03536          continue;
03537       }
03538 
03539       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s;%s",
03540          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername, cur_member->state_interface);
03541 
03542       ao2_ref(cur_member, -1);
03543 
03544       if (res != strlen(value + value_len)) {
03545          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03546          break;
03547       }
03548       value_len += res;
03549    }
03550    ao2_iterator_destroy(&mem_iter);
03551    
03552    if (value_len && !cur_member) {
03553       if (ast_db_put(pm_family, pm_queue->name, value))
03554          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03555    } else
03556       /* Delete the entry if the queue is empty or there is an error */
03557       ast_db_del(pm_family, pm_queue->name);
03558 }

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

find the entry with the best metric, or NULL

Definition at line 2107 of file app_queue.c.

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

02108 {
02109    struct callattempt *best = NULL, *cur;
02110 
02111    for (cur = outgoing; cur; cur = cur->q_next) {
02112       if (cur->stillgoing &&              /* Not already done */
02113          !cur->chan &&              /* Isn't already going */
02114          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
02115          best = cur;
02116       }
02117    }
02118 
02119    return best;
02120 }

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

References alloc_queue(), ao2_lock(), ao2_unlock(), AST_LIST_INSERT_HEAD, AST_LIST_TRAVERSE, ast_log(), clear_queue(), member_interface::interface, member_interface::list, LOG_DEBUG, LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, QUEUE_STRATEGY_RINGALL, call_queue::realtime, remove_queue(), strat2int(), call_queue::strategy, and ast_variable::value.

Referenced by load_realtime_queue().

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

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

Definition at line 1228 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), member::dynamic, call_queue::membercount, call_queue::members, remove_from_interfaces(), and member::state_interface.

Referenced by destroy_queue().

01229 {
01230    /* Free non-dynamic members */
01231    struct member *cur;
01232    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01233 
01234    while ((cur = ao2_iterator_next(&mem_iter))) {
01235       if (all || !cur->dynamic) {
01236          ao2_unlink(q->members, cur);
01237          remove_from_interfaces(cur->state_interface);
01238          q->membercount--;
01239       }
01240       ao2_ref(cur, -1);
01241    }
01242    ao2_iterator_destroy(&mem_iter);
01243 }

static enum queue_member_status get_member_status ( struct call_queue q,
int  max_penalty 
) [static]

Check if members are available.

This function checks to see if members are available to be called. If any member is available, the function immediately returns QUEUE_NORMAL. If no members are available, the appropriate reason why is returned

Definition at line 584 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, call_queue::members, member::paused, member::penalty, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_NORMAL, and member::status.

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

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

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

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

05244 {
05245    char *queuename, *interface, *membername = NULL, *state_interface = NULL;
05246    int penalty;
05247 
05248    if ((argc != 6) && (argc != 8) && (argc != 10) && (argc != 12)) {
05249       return RESULT_SHOWUSAGE;
05250    } else if (strcmp(argv[4], "to")) {
05251       return RESULT_SHOWUSAGE;
05252    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
05253       return RESULT_SHOWUSAGE;
05254    } else if ((argc == 10) && strcmp(argv[8], "as")) {
05255       return RESULT_SHOWUSAGE;
05256    } else if ((argc == 12) && strcmp(argv[10], "state_interface")) {
05257       return RESULT_SHOWUSAGE;
05258    }
05259 
05260    queuename = argv[5];
05261    interface = argv[3];
05262    if (argc >= 8) {
05263       if (sscanf(argv[7], "%30d", &penalty) == 1) {
05264          if (penalty < 0) {
05265             ast_cli(fd, "Penalty must be >= 0\n");
05266             penalty = 0;
05267          }
05268       } else {
05269          ast_cli(fd, "Penalty must be an integer >= 0\n");
05270          penalty = 0;
05271       }
05272    } else {
05273       penalty = 0;
05274    }
05275 
05276    if (argc >= 10) {
05277       membername = argv[9];
05278    }
05279 
05280    if (argc >= 12) {
05281       state_interface = argv[11];
05282    }
05283 
05284    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members, state_interface)) {
05285    case RES_OKAY:
05286       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
05287       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
05288       return RESULT_SUCCESS;
05289    case RES_EXISTS:
05290       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
05291       return RESULT_FAILURE;
05292    case RES_NOSUCHQUEUE:
05293       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
05294       return RESULT_FAILURE;
05295    case RES_OUTOFMEMORY:
05296       ast_cli(fd, "Out of memory\n");
05297       return RESULT_FAILURE;
05298    default:
05299       return RESULT_FAILURE;
05300    }
05301 }

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

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

05337 {
05338    char *queuename, *interface;
05339 
05340    if (argc != 6) {
05341       return RESULT_SHOWUSAGE;
05342    } else if (strcmp(argv[4], "from")) {
05343       return RESULT_SHOWUSAGE;
05344    }
05345 
05346    queuename = argv[5];
05347    interface = argv[3];
05348 
05349    switch (remove_from_queue(queuename, interface)) {
05350    case RES_OKAY:
05351       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
05352       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
05353       return RESULT_SUCCESS;
05354    case RES_EXISTS:
05355       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
05356       return RESULT_FAILURE;
05357    case RES_NOSUCHQUEUE:
05358       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
05359       return RESULT_FAILURE;
05360    case RES_OUTOFMEMORY:
05361       ast_cli(fd, "Out of memory\n");
05362       return RESULT_FAILURE;
05363    case RES_NOT_DYNAMIC:
05364       ast_cli(fd, "Member not dynamic\n");
05365       return RESULT_FAILURE;
05366    default:
05367       return RESULT_FAILURE;
05368    }
05369 }

static void* handle_statechange ( struct statechange sc  )  [static]

set a member's status based on device state of that member's interface

Definition at line 692 of file app_queue.c.

References ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdupa, statechange::dev, devstate2str(), member_interface::interface, member_interface::list, LOG_DEBUG, option_debug, statechange::state, and update_status().

Referenced by device_state_thread().

00693 {
00694    struct member_interface *curint;
00695    char *loc;
00696    char *technology;
00697    char interface[80];
00698 
00699    technology = ast_strdupa(sc->dev);
00700    loc = strchr(technology, '/');
00701    if (loc) {
00702       *loc++ = '\0';
00703    } else {
00704       return NULL;
00705    }
00706 
00707    AST_LIST_LOCK(&interfaces);
00708    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00709       char *slash_pos;
00710       ast_copy_string(interface, curint->interface, sizeof(interface));
00711       if ((slash_pos = strchr(interface, '/')))
00712          if ((slash_pos = strchr(slash_pos + 1, '/')))
00713             *slash_pos = '\0';
00714 
00715       if (!strcasecmp(interface, sc->dev))
00716          break;
00717    }
00718    AST_LIST_UNLOCK(&interfaces);
00719 
00720    if (!curint) {
00721       if (option_debug > 2)
00722          ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s) but we don't care because they're not a member of any queue.\n", technology, loc, sc->state, devstate2str(sc->state));
00723       return NULL;
00724    }
00725 
00726    if (option_debug)
00727       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00728 
00729    update_status(sc->dev, sc->state);
00730 
00731    return NULL;
00732 }

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

Definition at line 1801 of file app_queue.c.

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

01802 {
01803    struct callattempt *oo;
01804 
01805    while (outgoing) {
01806       /* Hangup any existing lines we have open */
01807       if (outgoing->chan && (outgoing->chan != exception))
01808          ast_hangup(outgoing->chan);
01809       oo = outgoing;
01810       outgoing = outgoing->q_next;
01811       if (oo->member)
01812          ao2_ref(oo->member, -1);
01813       free(oo);
01814    }
01815 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 868 of file app_queue.c.

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ao2_container_alloc(), ast_copy_string(), call_queue::autofill, call_queue::context, call_queue::dead, DEFAULT_RETRY, call_queue::eventwhencalled, call_queue::found, call_queue::joinempty, call_queue::leavewhenempty, call_queue::maskmemberstatus, MAX_PERIODIC_ANNOUNCEMENTS, call_queue::maxlen, member_cmp_fn(), member_hash_fn(), call_queue::membercount, call_queue::memberdelay, call_queue::members, call_queue::moh, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::periodicannouncefrequency, QUEUE_STRATEGY_RRORDERED, call_queue::reportholdtime, call_queue::retry, call_queue::ringinuse, call_queue::ringlimit, call_queue::roundingseconds, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_calls, call_queue::sound_holdtime, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_next, call_queue::sound_periodicannounce, call_queue::sound_reporthold, call_queue::sound_seconds, call_queue::sound_thanks, call_queue::sound_thereare, call_queue::strategy, call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by reload_queues().

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

static void insert_entry ( struct call_queue q,
struct queue_ent prev,
struct queue_ent new,
int *  pos 
) [inline, static]

Insert the 'new' entry after the 'prev' entry of queue 'q'.

Definition at line 548 of file app_queue.c.

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

Referenced by join_queue().

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

static char* int2strat ( int  strategy  )  [static]

Definition at line 502 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

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

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

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

03493 {
03494    struct member *mem;
03495    struct ao2_iterator mem_iter;
03496 
03497    if (!q)
03498       return NULL;
03499 
03500    mem_iter = ao2_iterator_init(q->members, 0);
03501    while ((mem = ao2_iterator_next(&mem_iter))) {
03502       if (!strcasecmp(interface, mem->interface)) {
03503          ao2_iterator_destroy(&mem_iter);
03504          return mem;
03505       }
03506       ao2_ref(mem, -1);
03507    }
03508    ao2_iterator_destroy(&mem_iter);
03509 
03510    return NULL;
03511 }

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

Definition at line 960 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member_interface::list, call_queue::members, and member::state_interface.

Referenced by remove_from_interfaces().

00961 {
00962    struct call_queue *q;
00963    struct member *mem;
00964    struct ao2_iterator mem_iter;
00965    int ret = 0;
00966 
00967    AST_LIST_LOCK(&queues);
00968    AST_LIST_TRAVERSE(&queues, q, list) {
00969       ao2_lock(q);
00970       mem_iter = ao2_iterator_init(q->members, 0);
00971       while ((mem = ao2_iterator_next(&mem_iter))) {
00972          if (!strcasecmp(mem->state_interface, interface)) {
00973             ao2_ref(mem, -1);
00974             ret = 1;
00975             break;
00976          }
00977          ao2_ref(mem, -1);
00978       }
00979       ao2_iterator_destroy(&mem_iter);
00980       ao2_unlock(q);
00981       if (ret)
00982          break;
00983    }
00984    AST_LIST_UNLOCK(&queues);
00985 
00986    return ret;
00987 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

A simple process, really. Count the number of members who are available to take our call and then see if we are in a position in the queue at which a member could accept our call.

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

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

Referenced by queue_exec(), and wait_our_turn().

02554 {
02555    struct queue_ent *ch;
02556    int res;
02557    int avl;
02558    int idx = 0;
02559    /* This needs a lock. How many members are available to be served? */
02560    ao2_lock(qe->parent);
02561 
02562    avl = num_available_members(qe->parent);
02563 
02564    ch = qe->parent->head;
02565 
02566    if (option_debug) {
02567       ast_log(LOG_DEBUG, "There %s %d available %s.\n", avl != 1 ? "are" : "is", avl, avl != 1 ? "members" : "member");
02568    }
02569 
02570    while ((idx < avl) && (ch) && (ch != qe)) {
02571       if (!ch->pending)
02572          idx++;
02573       ch = ch->next;       
02574    }
02575 
02576    ao2_unlock(qe->parent);
02577    /* If the queue entry is within avl [the number of available members] calls from the top ... 
02578     * Autofill and position check added to support autofill=no (as only calls
02579     * from the front of the queue are valid when autofill is disabled)
02580     */
02581    if (ch && idx < avl && (qe->parent->autofill || qe->pos == 1)) {
02582       if (option_debug)
02583          ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02584       res = 1;
02585    } else {
02586       if (option_debug)
02587          ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02588       res = 0;
02589    }
02590 
02591    return res;
02592 }

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

Definition at line 1515 of file app_queue.c.

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

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

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1760 of file app_queue.c.

References ao2_lock(), ao2_unlock(), ast_log(), queue_ent::chan, call_queue::count, call_queue::dead, EVENT_FLAG_CALL, call_queue::head, LOG_DEBUG, manager_event(), call_queue::name, 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().

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

static int load_module ( void   )  [static]

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

05626 {
05627    int res;
05628 
05629    if (!reload_queues())
05630       return AST_MODULE_LOAD_DECLINE;
05631 
05632    if (queue_persistent_members)
05633       reload_queue_members();
05634 
05635    ast_mutex_init(&device_state.lock);
05636    ast_cond_init(&device_state.cond, NULL);
05637    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05638 
05639    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05640    res = ast_register_application(app, queue_exec, synopsis, descrip);
05641    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05642    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05643    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05644    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05645    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05646    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05647    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05648    res |= ast_manager_register("QueueMemberCount", 0, manager_queue_member_count, "Queue Member Count");
05649    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05650    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05651    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05652    res |= ast_custom_function_register(&queueagentcount_function);
05653    res |= ast_custom_function_register(&queuemembercount_function);
05654    res |= ast_custom_function_register(&queuememberlist_function);
05655    res |= ast_custom_function_register(&queuewaitingcount_function);
05656    res |= ast_devstate_add(statechange_queue, NULL);
05657 
05658    return res;
05659 }

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

Definition at line 1465 of file app_queue.c.

References ast_config_destroy(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), 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().

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

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

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

05136 {
05137    const char *queuename, *interface, *penalty_s, *paused_s, *membername, *state_interface;
05138    int paused, penalty = 0;
05139 
05140    queuename = astman_get_header(m, "Queue");
05141    interface = astman_get_header(m, "Interface");
05142    penalty_s = astman_get_header(m, "Penalty");
05143    paused_s = astman_get_header(m, "Paused");
05144    membername = astman_get_header(m, "MemberName");
05145    state_interface = astman_get_header(m, "StateInterface");
05146 
05147    if (ast_strlen_zero(queuename)) {
05148       astman_send_error(s, m, "'Queue' not specified.");
05149       return 0;
05150    }
05151 
05152    if (ast_strlen_zero(interface)) {
05153       astman_send_error(s, m, "'Interface' not specified.");
05154       return 0;
05155    }
05156 
05157    if (ast_strlen_zero(penalty_s))
05158       penalty = 0;
05159    else if (sscanf(penalty_s, "%30d", &penalty) != 1 || penalty < 0)
05160       penalty = 0;
05161 
05162    if (ast_strlen_zero(paused_s))
05163       paused = 0;
05164    else
05165       paused = abs(ast_true(paused_s));
05166 
05167    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members, state_interface)) {
05168    case RES_OKAY:
05169       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
05170       astman_send_ack(s, m, "Added interface to queue");
05171       break;
05172    case RES_EXISTS:
05173       astman_send_error(s, m, "Unable to add interface: Already there");
05174       break;
05175    case RES_NOSUCHQUEUE:
05176       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
05177       break;
05178    case RES_OUTOFMEMORY:
05179       astman_send_error(s, m, "Out of memory");
05180       break;
05181    }
05182 
05183    return 0;
05184 }

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

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

05221 {
05222    const char *queuename, *interface, *paused_s;
05223    int paused;
05224 
05225    interface = astman_get_header(m, "Interface");
05226    paused_s = astman_get_header(m, "Paused");
05227    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
05228 
05229    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
05230       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
05231       return 0;
05232    }
05233 
05234    paused = abs(ast_true(paused_s));
05235 
05236    if (set_member_paused(queuename, interface, paused))
05237       astman_send_error(s, m, "Interface not found");
05238    else
05239       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
05240    return 0;
05241 }

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

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

05481 {
05482    char buffer[256] = "";
05483    const char *queuename = astman_get_header(m,"Queue");
05484 
05485    if (ast_strlen_zero(queuename)) {
05486          astman_send_error(s, m, "'Queue' not specified.");
05487          return 0;
05488    }
05489    if (qmc_handler(queuename, buffer, sizeof(buffer)) == RESULT_SUCCESS) {
05490          astman_send_ack(s, m, buffer);
05491          return RESULT_SUCCESS;
05492    } else {
05493          astman_send_error(s, m, "Queue not found.");
05494          return 0;
05495    }
05496 }

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

Definition at line 5029 of file app_queue.c.

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

Referenced by load_module().

05030 {
05031    char *a[] = { "queue", "show" };
05032 
05033    __queues_show(s, 1, -1, 2, a);
05034    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
05035 
05036    return RESULT_SUCCESS;
05037 }

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

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

05041 {
05042    time_t now;
05043    int pos;
05044    const char *id = astman_get_header(m,"ActionID");
05045    const char *queuefilter = astman_get_header(m,"Queue");
05046    const char *memberfilter = astman_get_header(m,"Member");
05047    char idText[256] = "";
05048    struct call_queue *q;
05049    struct queue_ent *qe;
05050    float sl = 0;
05051    struct member *mem;
05052    struct ao2_iterator mem_iter;
05053 
05054    astman_send_ack(s, m, "Queue status will follow");
05055    time(&now);
05056    AST_LIST_LOCK(&queues);
05057    if (!ast_strlen_zero(id))
05058       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
05059 
05060    AST_LIST_TRAVERSE(&queues, q, list) {
05061       ao2_lock(q);
05062 
05063       /* List queue properties */
05064       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
05065          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
05066          astman_append(s, "Event: QueueParams\r\n"
05067             "Queue: %s\r\n"
05068             "Max: %d\r\n"
05069             "Calls: %d\r\n"
05070             "Holdtime: %d\r\n"
05071             "Completed: %d\r\n"
05072             "Abandoned: %d\r\n"
05073             "ServiceLevel: %d\r\n"
05074             "ServicelevelPerf: %2.1f\r\n"
05075             "RingLimit: %d\r\n"
05076             "Weight: %d\r\n"
05077             "%s"
05078             "\r\n",
05079             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
05080             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
05081          /* List Queue Members */
05082          mem_iter = ao2_iterator_init(q->members, 0);
05083          while ((mem = ao2_iterator_next(&mem_iter))) {
05084             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
05085                astman_append(s, "Event: QueueMember\r\n"
05086                   "Queue: %s\r\n"
05087                   "Name: %s\r\n"
05088                   "Location: %s\r\n"
05089                   "Membership: %s\r\n"
05090                   "Penalty: %d\r\n"
05091                   "CallsTaken: %d\r\n"
05092                   "LastCall: %d\r\n"
05093                   "Status: %d\r\n"
05094                   "Paused: %d\r\n"
05095                   "%s"
05096                   "\r\n",
05097                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
05098                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
05099             }
05100             ao2_ref(mem, -1);
05101          }
05102          ao2_iterator_destroy(&mem_iter);
05103          /* List Queue Entries */
05104          pos = 1;
05105          for (qe = q->head; qe; qe = qe->next) {
05106             astman_append(s, "Event: QueueEntry\r\n"
05107                "Queue: %s\r\n"
05108                "Position: %d\r\n"
05109                "Channel: %s\r\n"
05110                "CallerID: %s\r\n"
05111                "CallerIDName: %s\r\n"
05112                "Wait: %ld\r\n"
05113                "%s"
05114                "\r\n",
05115                q->name, pos++, qe->chan->name,
05116                S_OR(qe->chan->cid.cid_num, "unknown"),
05117                S_OR(qe->chan->cid.cid_name, "unknown"),
05118                (long) (now - qe->start), idText);
05119          }
05120       }
05121       ao2_unlock(q);
05122    }
05123 
05124    astman_append(s,
05125       "Event: QueueStatusComplete\r\n"
05126       "%s"
05127       "\r\n",idText);
05128 
05129    AST_LIST_UNLOCK(&queues);
05130 
05131 
05132    return RESULT_SUCCESS;
05133 }

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

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

05187 {
05188    const char *queuename, *interface;
05189 
05190    queuename = astman_get_header(m, "Queue");
05191    interface = astman_get_header(m, "Interface");
05192 
05193    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
05194       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
05195       return 0;
05196    }
05197 
05198    switch (remove_from_queue(queuename, interface)) {
05199    case RES_OKAY:
05200       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
05201       astman_send_ack(s, m, "Removed interface from queue");
05202       break;
05203    case RES_EXISTS:
05204       astman_send_error(s, m, "Unable to remove interface: Not there");
05205       break;
05206    case RES_NOSUCHQUEUE:
05207       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
05208       break;
05209    case RES_OUTOFMEMORY:
05210       astman_send_error(s, m, "Out of memory");
05211       break;
05212    case RES_NOT_DYNAMIC:
05213       astman_send_error(s, m, "Member not dynamic");
05214       break;
05215    }
05216 
05217    return 0;
05218 }

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

Definition at line 862 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00863 {
00864    struct member *mem1 = obj1, *mem2 = obj2;
00865    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH | CMP_STOP;
00866 }

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

Definition at line 850 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00851 {
00852    const struct member *mem = obj;
00853    const char *chname = strchr(mem->interface, '/');
00854    int ret = 0, i;
00855    if (!chname)
00856       chname = mem->interface;
00857    for (i = 0; i < 5 && chname[i]; i++)
00858       ret += compress_char(chname[i]) << (i * 6);
00859    return ret;
00860 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 481 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

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

static int num_available_members ( struct call_queue q  )  [static]

Get the number of members available to accept a call.

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

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

Referenced by compare_weight(), and is_our_turn().

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

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

Definition at line 1580 of file app_queue.c.

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

Referenced by say_periodic_announcement(), and say_position().

01581 {
01582    int res;
01583 
01584    if (ast_strlen_zero(filename)) {
01585       return 0;
01586    }
01587 
01588    if (!ast_fileexists(filename, NULL, chan->language)) {
01589       return 0;
01590    }
01591 
01592    ast_stopstream(chan);
01593 
01594    res = ast_streamfile(chan, filename, chan->language);
01595    if (!res)
01596       res = ast_waitstream(chan, AST_DIGIT_ANY);
01597 
01598    ast_stopstream(chan);
01599 
01600    return res;
01601 }

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

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

03810 {
03811    struct ast_module_user *lu;
03812    char *parse;
03813    int priority_jump = 0;
03814    int ignore_fail = 0;
03815    AST_DECLARE_APP_ARGS(args,
03816       AST_APP_ARG(queuename);
03817       AST_APP_ARG(interface);
03818       AST_APP_ARG(options);
03819    );
03820 
03821    if (ast_strlen_zero(data)) {
03822       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03823       return -1;
03824    }
03825 
03826    parse = ast_strdupa(data);
03827 
03828    AST_STANDARD_APP_ARGS(args, parse);
03829 
03830    lu = ast_module_user_add(chan);
03831 
03832    if (args.options) {
03833       if (strchr(args.options, 'j'))
03834          priority_jump = 1;
03835       if (strchr(args.options, 'i'))
03836          ignore_fail = 1;
03837    }
03838 
03839    if (ast_strlen_zero(args.interface)) {
03840       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03841       ast_module_user_remove(lu);
03842       return -1;
03843    }
03844 
03845    if (set_member_paused(args.queuename, args.interface, 1)) {
03846       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03847       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03848       if (priority_jump || ast_opt_priority_jumping) {
03849          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03850             ast_module_user_remove(lu);
03851             return 0;
03852          }
03853       }
03854       ast_module_user_remove(lu);
03855       if (ignore_fail) {
03856          return 0;
03857       } else {
03858          return -1;
03859       }
03860    }
03861 
03862    ast_module_user_remove(lu);
03863    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03864    return 0;
03865 }

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

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

04068 {
04069    struct ast_module_user *u;
04070    char *parse;
04071 
04072    AST_DECLARE_APP_ARGS(args,
04073       AST_APP_ARG(queuename);
04074       AST_APP_ARG(uniqueid);
04075       AST_APP_ARG(membername);
04076       AST_APP_ARG(event);
04077       AST_APP_ARG(params);
04078    );
04079 
04080    if (ast_strlen_zero(data)) {
04081       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
04082       return -1;
04083    }
04084 
04085    u = ast_module_user_add(chan);
04086 
04087    parse = ast_strdupa(data);
04088 
04089    AST_STANDARD_APP_ARGS(args, parse);
04090 
04091    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
04092        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
04093       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
04094       ast_module_user_remove(u);
04095       return -1;
04096    }
04097 
04098    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
04099       "%s", args.params ? args.params : "");
04100 
04101    ast_module_user_remove(u);
04102 
04103    return 0;
04104 }

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

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

05466 {
05467       struct member_count qmc;
05468       memset(&qmc, 0, sizeof(qmc));
05469       if (queue_member_count(queuename, &qmc) != 0) {
05470             return RESULT_FAILURE; 
05471       } else {
05472             snprintf(buffer, len, 
05473                          "valid:%d inuse:%d paused:%d active:%d free:%d all:%d",
05474                          qmc.valid, qmc.inuse, qmc.paused, qmc.active, qmc.free, qmc.all);   
05475             return RESULT_SUCCESS;
05476       }
05477 }

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

References call_queue::announcefrequency, AST_APP_ARG, 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(), queue_ent::chan, ast_channel::cid, ast_callerid::cid_num, queue_ent::digits, queue_ent::expire, get_member_status(), queue_ent::handled, is_our_turn(), join_queue(), queue_ent::last_periodic_announce_sound, queue_ent::last_periodic_announce_time, queue_ent::last_pos, queue_ent::last_pos_said, leave_queue(), call_queue::leavewhenempty, LOG_DEBUG, LOG_WARNING, queue_ent::max_penalty, call_queue::membercount, queue_ent::moh, ast_channel::name, queue_ent::opos, option_verbose, queue_ent::parent, parse(), pbx_builtin_getvar_helper(), call_queue::periodicannouncefrequency, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), queue_ent::ring_when_ringing, S_OR, say_periodic_announcement(), say_position(), set_queue_result(), queue_ent::start, stop, try_calling(), ast_channel::uniqueid, update_realtime_members(), queue_ent::valid_digits, VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

04119 {
04120    int res=-1;
04121    int ringing=0;
04122    struct ast_module_user *lu;
04123    const char *user_priority;
04124    const char *max_penalty_str;
04125    int prio;
04126    int max_penalty;
04127    enum queue_result reason = QUEUE_UNKNOWN;
04128    /* whether to exit Queue application after the timeout hits */
04129    int tries = 0;
04130    int noption = 0;
04131    char *parse;
04132    AST_DECLARE_APP_ARGS(args,
04133       AST_APP_ARG(queuename);
04134       AST_APP_ARG(options);
04135       AST_APP_ARG(url);
04136       AST_APP_ARG(announceoverride);
04137       AST_APP_ARG(queuetimeoutstr);
04138       AST_APP_ARG(agi);
04139    );
04140    /* Our queue entry */
04141    struct queue_ent qe = { 0 };
04142    
04143    if (ast_strlen_zero(data)) {
04144       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
04145       return -1;
04146    }
04147    
04148    parse = ast_strdupa(data);
04149    AST_STANDARD_APP_ARGS(args, parse);
04150 
04151    lu = ast_module_user_add(chan);
04152 
04153    /* Setup our queue entry */
04154    qe.start = time(NULL);
04155 
04156    /* set the expire time based on the supplied timeout; */
04157    if (!ast_strlen_zero(args.queuetimeoutstr))
04158       qe.expire = qe.start + atoi(args.queuetimeoutstr);
04159    else
04160       qe.expire = 0;
04161 
04162    /* Get the priority from the variable ${QUEUE_PRIO} */
04163    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
04164    if (user_priority) {
04165       if (sscanf(user_priority, "%30d", &prio) == 1) {
04166          if (option_debug)
04167             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
04168                chan->name, prio);
04169       } else {
04170          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
04171             user_priority, chan->name);
04172          prio = 0;
04173       }
04174    } else {
04175       if (option_debug > 2)
04176          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
04177       prio = 0;
04178    }
04179 
04180    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
04181    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
04182       if (sscanf(max_penalty_str, "%30d", &max_penalty) == 1) {
04183          if (option_debug)
04184             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
04185                chan->name, max_penalty);
04186       } else {
04187          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
04188             max_penalty_str, chan->name);
04189          max_penalty = 0;
04190       }
04191    } else {
04192       max_penalty = 0;
04193    }
04194 
04195    if (args.options && (strchr(args.options, 'r')))
04196       ringing = 1;
04197 
04198    if (ringing != 1 && args.options && (strchr(args.options, 'R'))) {
04199       qe.ring_when_ringing = 1;
04200    }
04201 
04202    if (option_debug)
04203       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
04204          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
04205 
04206    qe.chan = chan;
04207    qe.prio = prio;
04208    qe.max_penalty = max_penalty;
04209    qe.last_pos_said = 0;
04210    qe.last_pos = 0;
04211    qe.last_periodic_announce_time = time(NULL);
04212    qe.last_periodic_announce_sound = 0;
04213    qe.valid_digits = 0;
04214    if (!join_queue(args.queuename, &qe, &reason)) {
04215       int makeannouncement = 0;
04216 
04217       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
04218          S_OR(chan->cid.cid_num, ""));
04219 check_turns:
04220       if (ringing) {
04221          ast_indicate(chan, AST_CONTROL_RINGING);
04222       } else {
04223          ast_moh_start(chan, qe.moh, NULL);
04224       }
04225 
04226       /* This is the wait loop for callers 2 through maxlen */
04227       res = wait_our_turn(&qe, ringing, &reason);
04228       if (res)
04229          goto stop;
04230 
04231       for (;;) {
04232          /* This is the wait loop for the head caller*/
04233          /* To exit, they may get their call answered; */
04234          /* they may dial a digit from the queue context; */
04235          /* or, they may timeout. */
04236 
04237          enum queue_member_status stat;
04238 
04239          /* Leave if we have exceeded our queuetimeout */
04240          if (qe.expire && (time(NULL) >= qe.expire)) {
04241             record_abandoned(&qe);
04242             reason = QUEUE_TIMEOUT;
04243             res = 0;
04244             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04245             break;
04246          }
04247 
04248          if (makeannouncement) {
04249             /* Make a position announcement, if enabled */
04250             if (qe.parent->announcefrequency && !ringing)
04251                if ((res = say_position(&qe)))
04252                   goto stop;
04253 
04254          }
04255          makeannouncement = 1;
04256 
04257          /* Leave if we have exceeded our queuetimeout */
04258          if (qe.expire && (time(NULL) >= qe.expire)) {
04259             record_abandoned(&qe);
04260             reason = QUEUE_TIMEOUT;
04261             res = 0;
04262             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04263             break;
04264          }
04265          /* Make a periodic announcement, if enabled */
04266          if (qe.parent->periodicannouncefrequency && !ringing)
04267             if ((res = say_periodic_announcement(&qe)))
04268                goto stop;
04269 
04270          /* Leave if we have exceeded our queuetimeout */
04271          if (qe.expire && (time(NULL) >= qe.expire)) {
04272             record_abandoned(&qe);
04273             reason = QUEUE_TIMEOUT;
04274             res = 0;
04275             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04276             break;
04277          }
04278          /* Try calling all queue members for 'timeout' seconds */
04279          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
04280          if (res)
04281             goto stop;
04282 
04283          stat = get_member_status(qe.parent, qe.max_penalty);
04284 
04285          /* exit after 'timeout' cycle if 'n' option enabled */
04286          if (noption && tries >= qe.parent->membercount) {
04287             if (option_verbose > 2)
04288                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
04289             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04290             record_abandoned(&qe);
04291             reason = QUEUE_TIMEOUT;
04292             res = 0;
04293             break;
04294          }
04295 
04296          /* leave the queue if no agents, if enabled */
04297          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
04298             record_abandoned(&qe);
04299             reason = QUEUE_LEAVEEMPTY;
04300             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04301             res = 0;
04302             break;
04303          }
04304 
04305          /* leave the queue if no reachable agents, if enabled */
04306          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
04307             record_abandoned(&qe);
04308             reason = QUEUE_LEAVEUNAVAIL;
04309             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
04310             res = 0;
04311             break;
04312          }
04313 
04314          /* Leave if we have exceeded our queuetimeout */
04315          if (qe.expire && (time(NULL) >= qe.expire)) {
04316             record_abandoned(&qe);
04317             reason = QUEUE_TIMEOUT;
04318             res = 0;
04319             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
04320             break;
04321          }
04322 
04323          /* If using dynamic realtime members, we should regenerate the member list for this queue */
04324          update_realtime_members(qe.parent);
04325 
04326          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
04327          res = wait_a_bit(&qe);
04328          if (res)
04329             goto stop;
04330 
04331          /* Since this is a priority queue and
04332           * it is not sure that we are still at the head
04333           * of the queue, go and check for our turn again.
04334           */
04335          if (!is_our_turn(&qe)) {
04336             if (option_debug)
04337                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
04338                   qe.chan->name);
04339             goto check_turns;
04340          }
04341       }
04342 
04343 stop:
04344       if (res) {
04345          if (res < 0) {
04346             if (!qe.handled) {
04347                record_abandoned(&qe);
04348                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
04349                   "%d|%d|%ld", qe.pos, qe.opos,
04350                   (long) time(NULL) - qe.start);
04351             }
04352             res = -1;
04353          } else if (qe.valid_digits) {
04354             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
04355                "%s|%d", qe.digits, qe.pos);
04356          }
04357       }
04358 
04359       /* Don't allow return code > 0 */
04360       if (res >= 0) {
04361          res = 0; 
04362          if (ringing) {
04363             ast_indicate(chan, -1);
04364          } else {
04365             ast_moh_stop(chan);
04366          }        
04367          ast_stopstream(chan);
04368       }
04369       leave_queue(&qe);
04370       if (reason != QUEUE_UNKNOWN)
04371          set_queue_result(chan, reason);
04372    } else {
04373       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
04374       set_queue_result(chan, reason);
04375       res = 0;
04376    }
04377    if (qe.parent) {
04378       /* every queue_ent is given a reference to it's parent call_queue when it joins the queue.
04379        * This ref must be taken away right before the queue_ent is destroyed.  In this case
04380        * the queue_ent is about to be returned on the stack */
04381       ao2_ref(qe.parent, -1);
04382    }
04383    ast_module_user_remove(lu);
04384 
04385    return res;
04386 }

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

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

04397 {
04398    int count = 0;
04399    struct call_queue *q;
04400    struct ast_module_user *lu;
04401    struct member *m;
04402    struct ao2_iterator mem_iter;
04403    char *name, *item;
04404    enum qmc_status mode = QMC_VALID;
04405 
04406    buf[0] = '\0';
04407    
04408    if (ast_strlen_zero(data)) {
04409       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04410       return -1;
04411    }
04412 
04413    name = ast_strdupa(data);
04414 
04415    lu = ast_module_user_add(chan);
04416 
04417    if ((item = strchr(name, ':'))) {
04418       *item = '\0';
04419       item++;
04420    } else {
04421       item = "";
04422    }
04423 
04424    if (!strcasecmp(item, "valid")) {
04425       mode = QMC_VALID;
04426    } else  if (!strcasecmp(item, "paused")) {
04427       mode = QMC_PAUSED;
04428    } else  if (!strcasecmp(item, "active")) {
04429       mode = QMC_ACTIVE;
04430    } else  if (!strcasecmp(item, "free")) {
04431       mode = QMC_FREE;
04432    } else  if (!strcasecmp(item, "all")) {
04433       mode = QMC_ALL;
04434    }
04435 
04436    if ((q = load_realtime_queue(name))) {
04437       ao2_lock(q);
04438       mem_iter = ao2_iterator_init(q->members, 0);
04439       while ((m = ao2_iterator_next(&mem_iter))) {
04440          switch (mode) {
04441          case QMC_VALID:
04442             /* Count the queue members who are logged in and presently answering calls */
04443             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04444                count++;
04445             }
04446             break;
04447          case QMC_PAUSED:
04448             /* Count paused members */
04449             if (m->paused) {
04450                count++;
04451             }
04452             break;
04453          case QMC_ACTIVE:
04454             /* Count not paused members who are logged in and presently answering calls */
04455             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04456                count++;
04457             }
04458             break;
04459          case QMC_FREE:
04460             /* Count free members in the queue */
04461             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04462                count++;
04463             }
04464             break;
04465          default:
04466             count++;
04467             break;
04468          }
04469          ao2_ref(m, -1);
04470       }
04471       ao2_iterator_destroy(&mem_iter);
04472       ao2_unlock(q);
04473    } else
04474       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04475 
04476    snprintf(buf, len, "%d", count);
04477    ast_module_user_remove(lu);
04478 
04479    return 0;
04480 }

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

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

04526 {
04527    struct ast_module_user *u;
04528    struct call_queue *q;
04529    struct member *m;
04530 
04531    /* Ensure an otherwise empty list doesn't return garbage */
04532    buf[0] = '\0';
04533 
04534    if (ast_strlen_zero(data)) {
04535       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04536       return -1;
04537    }
04538    
04539    u = ast_module_user_add(chan);
04540 
04541    AST_LIST_LOCK(&queues);
04542    AST_LIST_TRAVERSE(&queues, q, list) {
04543       if (!strcasecmp(q->name, data)) {
04544          ao2_lock(q);
04545          break;
04546       }
04547    }
04548    AST_LIST_UNLOCK(&queues);
04549 
04550    if (q) {
04551       int buflen = 0, count = 0;
04552       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04553 
04554       while ((m = ao2_iterator_next(&mem_iter))) {
04555          /* strcat() is always faster than printf() */
04556          if (count++) {
04557             strncat(buf + buflen, ",", len - buflen - 1);
04558             buflen++;
04559          }
04560          strncat(buf + buflen, m->interface, len - buflen - 1);
04561          buflen += strlen(m->interface);
04562          /* Safeguard against overflow (negative length) */
04563          if (buflen >= len - 2) {
04564             ao2_ref(m, -1);
04565             ast_log(LOG_WARNING, "Truncating list\n");
04566             break;
04567          }
04568          ao2_ref(m, -1);
04569       }
04570       ao2_iterator_destroy(&mem_iter);
04571       ao2_unlock(q);
04572    } else
04573       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04574 
04575    /* We should already be terminated, but let's make sure. */
04576    buf[len - 1] = '\0';
04577    ast_module_user_remove(u);
04578 
04579    return 0;
04580 }

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

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

04483 {
04484    int count = 0;
04485    struct call_queue *q;
04486    struct ast_module_user *lu;
04487    struct ast_variable *var = NULL;
04488 
04489    buf[0] = '\0';
04490    
04491    if (ast_strlen_zero(data)) {
04492       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04493       return -1;
04494    }
04495 
04496    lu = ast_module_user_add(chan);
04497    
04498    AST_LIST_LOCK(&queues);
04499    AST_LIST_TRAVERSE(&queues, q, list) {
04500       if (!strcasecmp(q->name, data)) {
04501          ao2_lock(q);
04502          break;
04503       }
04504    }
04505    AST_LIST_UNLOCK(&queues);
04506 
04507    if (q) {
04508       count = q->count;
04509       ao2_unlock(q);
04510    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04511       /* if the queue is realtime but was not found in memory, this
04512        * means that the queue had been deleted from memory since it was 
04513        * "dead." This means it has a 0 waiting count
04514        */
04515       count = 0;
04516       ast_variables_destroy(var);
04517    } else
04518       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04519 
04520    snprintf(buf, len, "%d", count);
04521    ast_module_user_remove(lu);
04522    return 0;
04523 }

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

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

05424 {
05425       int res = 0;
05426       struct call_queue *q;
05427       struct member *m;
05428       struct ao2_iterator mem_iter;
05429       
05430       if ((q = load_realtime_queue(qname))) {
05431             ao2_lock(q);
05432             mem_iter = ao2_iterator_init(q->members, 0);
05433             while ((m = ao2_iterator_next(&mem_iter))) {
05434                   /* Count the queue members in use */
05435                   if (m->status == AST_DEVICE_INUSE) {
05436                         qmc->inuse++;
05437                   }
05438                   /* Count the queue members who are logged in and presently answering calls */
05439                   if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05440                         qmc->valid++;
05441                   }
05442                   /* Count paused members */
05443                   if (m->paused) {
05444                         qmc->paused++;
05445                   }
05446                   /* Count not paused members who are logged in and presently answering calls */
05447                   if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
05448                         qmc->active++;
05449                   }
05450                   /* Count free members in the queue */
05451                   if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
05452                         qmc->free++;
05453                   }
05454                   qmc->all++;
05455                   ao2_ref(m, -1);
05456             }
05457             ao2_unlock(q);
05458       } else {
05459             ast_log(LOG_WARNING, "Queue %s was not found\n", qname);
05460             res = -1;
05461       }
05462       return res;
05463 }

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

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

Referenced by reload_queues().

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

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

Definition at line 4995 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04996 {
04997    return __queues_show(NULL, 0, fd, argc, argv);
04998 }

static void queue_transfer_destroy ( void *  data  )  [static]

Definition at line 2765 of file app_queue.c.

References ast_free.

02766 {
02767    struct queue_transfer_ds *qtds = data;
02768    ast_free(qtds);
02769 }

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

02789 {
02790    struct queue_transfer_ds *qtds = data;
02791    struct queue_ent *qe = qtds->qe;
02792    struct member *member = qtds->member;
02793    time_t callstart = qtds->starttime;
02794    int callcompletedinsl = qtds->callcompletedinsl;
02795    struct ast_datastore *datastore;
02796 
02797    ast_queue_log(qe->parent->name, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
02798             new_chan->exten, new_chan->context, (long) (callstart - qe->start),
02799             (long) (time(NULL) - callstart));
02800 
02801    update_queue(qe->parent, member, callcompletedinsl);
02802    
02803    /* No need to lock the channels because they are already locked in ast_do_masquerade */
02804    if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {
02805       ast_channel_datastore_remove(old_chan, datastore);
02806    } else {
02807       ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
02808    }
02809 }

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

Definition at line 1745 of file app_queue.c.

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

01746 {
01747    int oldvalue;
01748 
01749    /* Calculate holdtime using an exponential average */
01750    /* Thanks to SRT for this contribution */
01751    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01752 
01753    ao2_lock(qe->parent);
01754    oldvalue = qe->parent->holdtime;
01755    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01756    ao2_unlock(qe->parent);
01757 }

static void record_abandoned ( struct queue_ent qe  )  [static]

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

02229 {
02230    ao2_lock(qe->parent);
02231    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02232       "Queue: %s\r\n"
02233       "Uniqueid: %s\r\n"
02234       "Position: %d\r\n"
02235       "OriginalPosition: %d\r\n"
02236       "HoldTime: %d\r\n",
02237       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02238 
02239    qe->parent->callsabandoned++;
02240    ao2_unlock(qe->parent);
02241 }

static int reload ( void   )  [static]

Definition at line 5661 of file app_queue.c.

References reload_queues().

05662 {
05663    reload_queues();
05664    return 0;
05665 }

static void reload_queue_members ( void   )  [static]

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

03713 {
03714    char *cur_ptr;
03715    char *queue_name;
03716    char *member;
03717    char *interface;
03718    char *membername = NULL;
03719    char *state_interface;
03720    char *penalty_tok;
03721    int penalty = 0;
03722    char *paused_tok;
03723    int paused = 0;
03724    struct ast_db_entry *db_tree;
03725    struct ast_db_entry *entry;
03726    struct call_queue *cur_queue;
03727    char queue_data[PM_MAX_LEN];
03728 
03729    AST_LIST_LOCK(&queues);
03730 
03731    /* Each key in 'pm_family' is the name of a queue */
03732    db_tree = ast_db_gettree(pm_family, NULL);
03733    for (entry = db_tree; entry; entry = entry->next) {
03734 
03735       queue_name = entry->key + strlen(pm_family) + 2;
03736 
03737       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03738          ao2_lock(cur_queue);
03739          if (!strcmp(queue_name, cur_queue->name))
03740             break;
03741          ao2_unlock(cur_queue);
03742       }
03743       
03744       if (!cur_queue)
03745          cur_queue = load_realtime_queue(queue_name);
03746 
03747       if (!cur_queue) {
03748          /* If the queue no longer exists, remove it from the
03749           * database */
03750          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03751          ast_db_del(pm_family, queue_name);
03752          continue;
03753       } else
03754          ao2_unlock(cur_queue);
03755 
03756       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03757          continue;
03758 
03759       cur_ptr = queue_data;
03760       while ((member = strsep(&cur_ptr, "|"))) {
03761          if (ast_strlen_zero(member))
03762             continue;
03763 
03764          interface = strsep(&member, ";");
03765          penalty_tok = strsep(&member, ";");
03766          paused_tok = strsep(&member, ";");
03767          membername = strsep(&member, ";");
03768          state_interface = strsep(&member,";");
03769 
03770          if (!penalty_tok) {
03771             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03772             break;
03773          }
03774          penalty = strtol(penalty_tok, NULL, 10);
03775          if (errno == ERANGE) {
03776             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03777             break;
03778          }
03779          
03780          if (!paused_tok) {
03781             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03782             break;
03783          }
03784          paused = strtol(paused_tok, NULL, 10);
03785          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03786             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03787             break;
03788          }
03789          if (ast_strlen_zero(membername))
03790             membername = interface;
03791 
03792          if (option_debug)
03793             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03794          
03795          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0, state_interface) == RES_OUTOFMEMORY) {
03796             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03797             break;
03798          }
03799       }
03800    }
03801 
03802    AST_LIST_UNLOCK(&queues);
03803    if (db_tree) {
03804       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03805       ast_db_freetree(db_tree);
03806    }
03807 }

static int reload_queues ( void   )  [static]

Definition at line 4625 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_RINGALL, QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), member::state_interface, strat2int(), call_queue::strategy, and var.

Referenced by load_module(), and reload().

04626 {
04627    struct call_queue *q;
04628    struct ast_config *cfg;
04629    char *cat, *tmp;
04630    struct ast_variable *var;
04631    struct member *cur, *newm;
04632    struct ao2_iterator mem_iter;
04633    int new;
04634    const char *general_val = NULL;
04635    char *parse;
04636    char *interface, *state_interface;
04637    char *membername = NULL;
04638    int penalty;
04639    AST_DECLARE_APP_ARGS(args,
04640       AST_APP_ARG(interface);
04641       AST_APP_ARG(penalty);
04642       AST_APP_ARG(membername);
04643       AST_APP_ARG(state_interface);
04644    );
04645    
04646    if (!(cfg = ast_config_load("queues.conf"))) {
04647       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04648       return 0;
04649    }
04650    AST_LIST_LOCK(&queues);
04651    use_weight=0;
04652    /* Mark all non-realtime queues as dead for the moment */
04653    AST_LIST_TRAVERSE(&queues, q, list) {
04654       if (!q->realtime) {
04655          q->dead = 1;
04656          q->found = 0;
04657       }
04658    }
04659 
04660    /* Chug through config file */
04661    cat = NULL;
04662    while ((cat = ast_category_browse(cfg, cat)) ) {
04663       if (!strcasecmp(cat, "general")) {  
04664          /* Initialize global settings */
04665          queue_debug = 0;
04666          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04667             queue_debug = ast_true(general_val);
04668          queue_persistent_members = 0;
04669          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04670             queue_persistent_members = ast_true(general_val);
04671          autofill_default = 0;
04672          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04673             autofill_default = ast_true(general_val);
04674          montype_default = 0;
04675          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04676             if (!strcasecmp(general_val, "mixmonitor"))
04677                montype_default = 1;
04678       } else { /* Define queue */
04679          /* Look for an existing one */
04680          AST_LIST_TRAVERSE(&queues, q, list) {
04681             if (!strcmp(q->name, cat))
04682                break;
04683          }
04684          if (!q) {
04685             /* Make one then */
04686             if (!(q = alloc_queue(cat))) {
04687                /* TODO: Handle memory allocation failure */
04688             }
04689             new = 1;
04690          } else
04691             new = 0;
04692          if (q) {
04693             const char *tmpvar;
04694             if (!new)
04695                ao2_lock(q);
04696             /* Check if a queue with this name already exists */
04697             if (q->found) {
04698                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04699                if (!new)
04700                   ao2_unlock(q);
04701                continue;
04702             }
04703 
04704             /* Due to the fact that the "rrordered" strategy will have a different allocation
04705              * scheme for queue members, we must devise the queue's strategy before other initializations.
04706              * To be specific, the rrordered strategy needs to function like a linked list, meaning the ao2
04707              * container used will have only a single bucket instead of the typical number.
04708              */
04709             if ((tmpvar = ast_variable_retrieve(cfg, cat, "strategy"))) {
04710                q->strategy = strat2int(tmpvar);
04711                if (q->strategy < 0) {
04712                   ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n", tmpvar, q->name);
04713                   q->strategy = QUEUE_STRATEGY_RINGALL;
04714                }
04715             } else {
04716                q->strategy = QUEUE_STRATEGY_RINGALL;
04717             }
04718 
04719             /* Re-initialize the queue, and clear statistics */
04720             init_queue(q);
04721             clear_queue(q);
04722             mem_iter = ao2_iterator_init(q->members, 0);
04723             while ((cur = ao2_iterator_next(&mem_iter))) {
04724                if (!cur->dynamic) {
04725                   cur->delme = 1;
04726                }
04727                ao2_ref(cur, -1);
04728             }
04729             ao2_iterator_destroy(&mem_iter);
04730             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04731                if (!strcasecmp(var->name, "member")) {
04732                   struct member tmpmem;
04733                   membername = NULL;
04734 
04735                   if (ast_strlen_zero(var->value)) {
04736                      ast_log(LOG_WARNING, "Empty queue member definition at line %d. Moving on!\n", var->lineno);
04737                      continue;
04738                   }
04739 
04740                   /* Add a new member */
04741                   if (!(parse = ast_strdup(var->value))) {
04742                      continue;
04743                   }
04744                   
04745                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04746 
04747                   interface = args.interface;
04748                   if (!ast_strlen_zero(args.penalty)) {
04749                      tmp = ast_skip_blanks(args.penalty);
04750                      penalty = atoi(tmp);
04751                      if (penalty < 0) {
04752                         penalty = 0;
04753                      }
04754                   } else
04755                      penalty = 0;
04756 
04757                   if (!ast_strlen_zero(args.membername)) {
04758                      membername = ast_skip_blanks(args.membername);
04759                   }
04760 
04761                   if (!ast_strlen_zero(args.state_interface)) {
04762                      state_interface = ast_skip_blanks(args.state_interface);
04763                   } else {
04764                      state_interface = interface;
04765                   }
04766 
04767                   /* Find the old position in the list */
04768                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04769                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04770 
04771                   /* Only attempt removing from interfaces list if the new state_interface is different than the old one */
04772                   if (cur && strcasecmp(cur->state_interface, state_interface)) {
04773                      remove_from_interfaces(cur->state_interface);
04774                   }
04775 
04776                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
04777                   if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
04778                      add_to_interfaces(state_interface);
04779                   }
04780                   ao2_link(q->members, newm);
04781                   ao2_ref(newm, -1);
04782                   newm = NULL;
04783 
04784                   if (cur)
04785                      ao2_ref(cur, -1);
04786                   else {
04787                      q->membercount++;
04788                   }
04789                   ast_free(parse);
04790                } else {
04791                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04792                }
04793             }
04794 
04795             /* Free remaining members marked as delme */
04796             mem_iter = ao2_iterator_init(q->members, 0);
04797             while ((cur = ao2_iterator_next(&mem_iter))) {
04798                if (! cur->delme) {
04799                   ao2_ref(cur, -1);
04800                   continue;
04801                }
04802 
04803                q->membercount--;
04804                ao2_unlink(q->members, cur);
04805                remove_from_interfaces(cur->state_interface);
04806                ao2_ref(cur, -1);
04807             }
04808             ao2_iterator_destroy(&mem_iter);
04809 
04810             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04811                rr_dep_warning();
04812 
04813             if (new) {
04814                AST_LIST_INSERT_HEAD(&queues, q, list);
04815             } else
04816                ao2_unlock(q);
04817          }
04818       }
04819    }
04820    ast_config_destroy(cfg);
04821    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04822       if (q->dead) {
04823          AST_LIST_REMOVE_CURRENT(&queues, list);
04824          ao2_ref(q, -1);
04825       } else {
04826          ao2_lock(q);
04827          mem_iter = ao2_iterator_init(q->members, 0);
04828          while ((cur = ao2_iterator_next(&mem_iter))) {
04829             if (cur->dynamic)
04830                q->membercount++;
04831             cur->status = ast_device_state(cur->state_interface);
04832             ao2_ref(cur, -1);
04833          }
04834          ao2_iterator_destroy(&mem_iter);
04835          ao2_unlock(q);
04836       }
04837    }
04838    AST_LIST_TRAVERSE_SAFE_END;
04839    AST_LIST_UNLOCK(&queues);
04840    return 1;
04841 }

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

Definition at line 989 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), free, member_interface::interface, interface_exists_global(), 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().

00990 {
00991    struct member_interface *curint;
00992 
00993    if (interface_exists_global(interface))
00994       return 0;
00995 
00996    AST_LIST_LOCK(&interfaces);
00997    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00998       if (!strcasecmp(curint->interface, interface)) {
00999          if (option_debug)
01000             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
01001          AST_LIST_REMOVE_CURRENT(&interfaces, list);
01002          free(curint);
01003          break;
01004       }
01005    }
01006    AST_LIST_TRAVERSE_SAFE_END;
01007    AST_LIST_UNLOCK(&interfaces);
01008 
01009    return 0;
01010 }

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

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

03561 {
03562    struct call_queue *q;
03563    struct member *mem, tmpmem;
03564    int res = RES_NOSUCHQUEUE;
03565 
03566    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03567 
03568    AST_LIST_LOCK(&queues);
03569    AST_LIST_TRAVERSE(&queues, q, list) {
03570       ao2_lock(q);
03571       if (strcmp(q->name, queuename)) {
03572          ao2_unlock(q);
03573          continue;
03574       }
03575 
03576       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03577          /* XXX future changes should beware of this assumption!! */
03578          if (!mem->dynamic) {
03579             res = RES_NOT_DYNAMIC;
03580             ao2_ref(mem, -1);
03581             ao2_unlock(q);
03582             break;
03583          }
03584          q->membercount--;
03585          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03586             "Queue: %s\r\n"
03587             "Location: %s\r\n"
03588             "MemberName: %s\r\n",
03589             q->name, mem->interface, mem->membername);
03590          ao2_unlink(q->members, mem);
03591          remove_from_interfaces(mem->state_interface);
03592          ao2_ref(mem, -1);
03593 
03594          if (queue_persistent_members)
03595             dump_queue_members(q);
03596          
03597          res = RES_OKAY;
03598       } else {
03599          res = RES_EXISTS;
03600       }
03601       ao2_unlock(q);
03602       break;
03603    }
03604 
03605    AST_LIST_UNLOCK(&queues);
03606 
03607    return res;
03608 }

static void remove_queue ( struct call_queue q  )  [static]

removes a call_queue from the list of call_queues

Definition at line 529 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and leave_queue().

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

static int ring_entry ( struct queue_ent qe,
struct callattempt tmp,
int *  busies 
) [static]

Part 2 of ring_one.

Does error checking before attempting to request a channel and call a member. This function is only called from ring_one

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

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

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

02131 {
02132    int ret = 0;
02133 
02134    while (ret == 0) {
02135       struct callattempt *best = find_best(outgoing);
02136       if (!best) {
02137          if (option_debug)
02138             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
02139          break;
02140       }
02141       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02142          struct callattempt *cur;
02143          /* Ring everyone who shares this best metric (for ringall) */
02144          for (cur = outgoing; cur; cur = cur->q_next) {
02145             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
02146                if (option_debug)
02147                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
02148                ret |= ring_entry(qe, cur, busies);
02149             }
02150          }
02151       } else {
02152          /* Ring just the best channel */
02153          if (option_debug)
02154             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
02155          ret = ring_entry(qe, best, busies);
02156       }
02157    }
02158 
02159    return ret;
02160 }

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

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

02245 {
02246    if (option_verbose > 2)
02247       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02248 
02249    /* Stop ringing, and resume MOH if specified */
02250    if (qe->ring_when_ringing) {
02251       ast_indicate(qe->chan, -1);
02252       ast_moh_start(qe->chan, qe->moh, NULL);
02253    }
02254 
02255    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02256    if (qe->parent->autopause && pause) {
02257       if (!set_member_paused(qe->parent->name, interface, 1)) {
02258          if (option_verbose > 2)
02259             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02260       } else {
02261          if (option_verbose > 2)
02262             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02263       }
02264    }
02265    return;
02266 }

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

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

03926 {
03927    int res=-1;
03928    struct ast_module_user *lu;
03929    char *parse, *temppos = NULL;
03930    int priority_jump = 0;
03931    AST_DECLARE_APP_ARGS(args,
03932       AST_APP_ARG(queuename);
03933       AST_APP_ARG(interface);
03934       AST_APP_ARG(options);
03935    );
03936 
03937 
03938    if (ast_strlen_zero(data)) {
03939       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03940       return -1;
03941    }
03942 
03943    parse = ast_strdupa(data);
03944 
03945    AST_STANDARD_APP_ARGS(args, parse);
03946 
03947    lu = ast_module_user_add(chan);
03948 
03949    if (ast_strlen_zero(args.interface)) {
03950       args.interface = ast_strdupa(chan->name);
03951       temppos = strrchr(args.interface, '-');
03952       if (temppos)
03953          *temppos = '\0';
03954    }
03955 
03956    if (args.options) {
03957       if (strchr(args.options, 'j'))
03958          priority_jump = 1;
03959    }
03960 
03961    switch (remove_from_queue(args.queuename, args.interface)) {
03962    case RES_OKAY:
03963       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03964       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03965       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03966       res = 0;
03967       break;
03968    case RES_EXISTS:
03969       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03970       if (priority_jump || ast_opt_priority_jumping)
03971          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03972       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03973       res = 0;
03974       break;
03975    case RES_NOSUCHQUEUE:
03976       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03977       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03978       res = 0;
03979       break;
03980    case RES_NOT_DYNAMIC:
03981       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03982       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03983       res = 0;
03984       break;
03985    }
03986 
03987    ast_module_user_remove(lu);
03988 
03989    return res;
03990 }

static void rr_dep_warning ( void   )  [static]

Definition at line 471 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

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

static void rt_handle_member_record ( struct call_queue q,
char *  interface,
const char *  membername,
const char *  penalty_str,
const char *  paused_str,
const char *  state_interface 
) [static]

Definition at line 1181 of file app_queue.c.

References add_to_interfaces(), ao2_find(), ao2_ref(), ast_copy_string(), create_queue_member(), member::dead, member::interface, call_queue::membercount, call_queue::members, member::paused, member::penalty, member::realtime, remove_from_interfaces(), and member::state_interface.

Referenced by update_realtime_members().

01182 {
01183    struct member *m, tmpmem;
01184    int penalty = 0;
01185    int paused  = 0;
01186 
01187    if (penalty_str) {
01188       penalty = atoi(penalty_str);
01189       if (penalty < 0)
01190          penalty = 0;
01191    }
01192 
01193    if (paused_str) {
01194       paused = atoi(paused_str);
01195       if (paused < 0)
01196          paused = 0;
01197    }
01198 
01199    /* Find the member, or the place to put a new one. */
01200    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01201    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01202 
01203    /* Create a new one if not found, else update penalty */
01204    if (!m) {
01205       if ((m = create_queue_member(interface, membername, penalty, paused, state_interface))) {
01206          m->dead = 0;
01207          m->realtime = 1;
01208          add_to_interfaces(m->state_interface);
01209          ao2_link(q->members, m);
01210          ao2_ref(m, -1);
01211          m = NULL;
01212          q->membercount++;
01213       }
01214    } else {
01215       m->dead = 0;   /* Do not delete this one. */
01216       if (paused_str)
01217          m->paused = paused;
01218       if (strcasecmp(state_interface, m->state_interface)) {
01219          remove_from_interfaces(m->state_interface);
01220          ast_copy_string(m->state_interface, state_interface, sizeof(m->state_interface));
01221          add_to_interfaces(m->state_interface);
01222       }
01223       m->penalty = penalty;
01224       ao2_ref(m, -1);
01225    }
01226 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02187 {
02188    int res = 0;
02189    time_t now;
02190 
02191    /* Get the current time */
02192    time(&now);
02193 
02194    /* Check to see if it is time to announce */
02195    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02196       return 0;
02197 
02198    /* Stop the music on hold so we can play our own file */
02199    ast_moh_stop(qe->chan);
02200 
02201    if (option_verbose > 2)
02202       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02203 
02204    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02205    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02206       qe->last_periodic_announce_sound = 0;
02207    }
02208    
02209    /* play the announcement */
02210    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02211 
02212    if (res > 0 && !valid_exit(qe, res))
02213       res = 0;
02214 
02215    /* Resume Music on Hold if the caller is going to stay in the queue */
02216    if (!res)
02217       ast_moh_start(qe->chan, qe->moh, NULL);
02218 
02219    /* update last_periodic_announce_time */
02220    qe->last_periodic_announce_time = now;
02221 
02222    /* Update the current periodic announcement to the next announcement */
02223    qe->last_periodic_announce_sound++;
02224    
02225    return res;
02226 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1636 of file app_queue.c.

References call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ONCE, AST_DIGIT_ANY, ast_moh_start(), ast_moh_stop(), ast_say_number(), ast_verbose(), queue_ent::chan, call_queue::holdtime, 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().

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

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

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

03667 {
03668    int found = 0;
03669    struct call_queue *q;
03670    struct member *mem;
03671 
03672    /* Special event for when all queues are paused - individual events still generated */
03673    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03674    if (ast_strlen_zero(queuename))
03675       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03676 
03677    AST_LIST_LOCK(&queues);
03678    AST_LIST_TRAVERSE(&queues, q, list) {
03679       ao2_lock(q);
03680       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03681          if ((mem = interface_exists(q, interface))) {
03682             found++;
03683             if (mem->paused == paused)
03684                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03685             mem->paused = paused;
03686 
03687             if (queue_persistent_members)
03688                dump_queue_members(q);
03689 
03690             if (mem->realtime)
03691                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03692 
03693             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03694 
03695             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03696                "Queue: %s\r\n"
03697                "Location: %s\r\n"
03698                "MemberName: %s\r\n"
03699                "Paused: %d\r\n",
03700                   q->name, mem->interface, mem->membername, paused);
03701             ao2_ref(mem, -1);
03702          }
03703       }
03704       ao2_unlock(q);
03705    }
03706    AST_LIST_UNLOCK(&queues);
03707 
03708    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03709 }

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

sets the QUEUESTATUS channel variable

Definition at line 490 of file app_queue.c.

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

Referenced by queue_exec().

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

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

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

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

02827 {
02828    struct ast_datastore *ds;
02829    struct queue_transfer_ds *qtds = ast_calloc(1, sizeof(*qtds));
02830 
02831    if (!qtds) {
02832       ast_log(LOG_WARNING, "Memory allocation error!\n");
02833       return NULL;
02834    }
02835 
02836    ast_channel_lock(qe->chan);
02837    if (!(ds = ast_channel_datastore_alloc(&queue_transfer_info, NULL))) {
02838       ast_channel_unlock(qe->chan);
02839       ast_log(LOG_WARNING, "Unable to create transfer datastore. queue_log will not show attended transfer\n");
02840       return NULL;
02841    }
02842 
02843    qtds->qe = qe;
02844    /* This member is refcounted in try_calling, so no need to add it here, too */
02845    qtds->member = member;
02846    qtds->starttime = starttime;
02847    qtds->callcompletedinsl = callcompletedinsl;
02848    ds->data = qtds;
02849    ast_channel_datastore_add(qe->chan, ds);
02850    ast_channel_unlock(qe->chan);
02851    return ds;
02852 }

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

Producer of the statechange queue.

Definition at line 787 of file app_queue.c.

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

Referenced by load_module(), and unload_module().

00788 {
00789    struct statechange *sc;
00790 
00791    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00792       return 0;
00793 
00794    sc->state = state;
00795    strcpy(sc->dev, dev);
00796 
00797    ast_mutex_lock(&device_state.lock);
00798    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00799    ast_cond_signal(&device_state.cond);
00800    ast_mutex_unlock(&device_state.lock);
00801 
00802    return 0;
00803 }

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

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

02163 {
02164    struct callattempt *best = find_best(outgoing);
02165 
02166    if (best) {
02167       /* Ring just the best channel */
02168       if (option_debug)
02169          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
02170       qe->parent->rrpos = best->metric % 1000;
02171    } else {
02172       /* Just increment rrpos */
02173       if (qe->parent->wrapped) {
02174          /* No more channels, start over */
02175          qe->parent->rrpos = 0;
02176       } else {
02177          /* Prioritize next entry */
02178          qe->parent->rrpos++;
02179       }
02180    }
02181    qe->parent->wrapped = 0;
02182 
02183    return 0;
02184 }

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

Definition at line 514 of file app_queue.c.

References name, and strategies.

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

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

static int try_calling ( struct queue_ent qe,
const char *  options,
char *  announceoverride,
const char *  url,
int *  tries,
int *  noption,
const char *  agi 
) [static]

A large function which calls members, updates statistics, and bridges the caller and a member.

Here is the process of this function 1. Process any options passed to the Queue() application. Options here mean the third argument to Queue() 2. Iterate trough the members of the queue, creating a callattempt corresponding to each member. During this iteration, we also check the dialed_interfaces datastore to see if we have already attempted calling this member. If we have, we do not create a callattempt. This is in place to prevent call forwarding loops. Also during each iteration, we call calc_metric to determine which members should be rung when. 3. Call ring_one to place a call to the appropriate member(s) 4. Call wait_for_answer to wait for an answer. If no one answers, return. 5. Take care of any holdtime announcements, member delays, or other options which occur after a call has been answered. 6. Start the monitor or mixmonitor if the option is set 7. Remove the caller from the queue to allow other callers to advance 8. Bridge the call. 9. Do any post processing after the call has disconnected.

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 2879 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, QUEUE_STRATEGY_RRMEMORY, QUEUE_STRATEGY_RRORDERED, 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().

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

static int unload_module ( void   )  [static]

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

05589 {
05590    int res;
05591 
05592    if (device_state.thread != AST_PTHREADT_NULL) {
05593       device_state.stop = 1;
05594       ast_mutex_lock(&device_state.lock);
05595       ast_cond_signal(&device_state.cond);
05596       ast_mutex_unlock(&device_state.lock);
05597       pthread_join(device_state.thread, NULL);
05598    }
05599 
05600    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05601    res = ast_manager_unregister("QueueStatus");
05602    res |= ast_manager_unregister("Queues");
05603    res |= ast_manager_unregister("QueueAdd");
05604    res |= ast_manager_unregister("QueueRemove");
05605    res |= ast_manager_unregister("QueuePause");
05606    res |= ast_unregister_application(app_aqm);
05607    res |= ast_unregister_application(app_rqm);
05608    res |= ast_unregister_application(app_pqm);
05609    res |= ast_unregister_application(app_upqm);
05610    res |= ast_unregister_application(app_ql);
05611    res |= ast_unregister_application(app);
05612    res |= ast_custom_function_unregister(&queueagentcount_function);
05613    res |= ast_custom_function_unregister(&queuemembercount_function);
05614    res |= ast_custom_function_unregister(&queuememberlist_function);
05615    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05616    ast_devstate_del(statechange_queue, NULL);
05617 
05618    ast_module_user_hangup_all();
05619 
05620    clear_and_free_interfaces();
05621 
05622    return res;
05623 }

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

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

02679 {
02680    ao2_lock(q);
02681    time(&member->lastcall);
02682    member->calls++;
02683    q->callscompleted++;
02684    if (callcompletedinsl)
02685       q->callscompletedinsl++;
02686    ao2_unlock(q);
02687    return 0;
02688 }

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

Definition at line 1394 of file app_queue.c.

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

Referenced by set_member_paused().

01395 {
01396    struct ast_variable *var, *save;
01397    int ret = -1;
01398 
01399    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01400       return ret;
01401    save = var;
01402    while (var) {
01403       if (!strcmp(var->name, "uniqueid"))
01404          break;
01405       var = var->next;
01406    }
01407    if (var && !ast_strlen_zero(var->value)) {
01408       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01409          ret = 0;
01410    }
01411    ast_variables_destroy(save);
01412    return ret;
01413 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1415 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlink(), ao2_unlock(), ast_category_browse(), ast_config_destroy(), ast_load_realtime_multientry(), ast_log(), ast_variable_retrieve(), member::dead, member_interface::interface, LOG_DEBUG, call_queue::membercount, call_queue::members, call_queue::name, option_debug, member::realtime, remove_from_interfaces(), rt_handle_member_record(), S_OR, and member::state_interface.

Referenced by load_realtime_queue(), and queue_exec().

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

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

Definition at line 638 of file app_queue.c.

References ao2_iterator_destroy(), ao2_iterator_init(), ao2_iterator_next(), ao2_lock(), ao2_ref(), ao2_unlock(), ast_copy_string(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, 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().

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

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

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

03868 {
03869    struct ast_module_user *lu;
03870    char *parse;
03871    int priority_jump = 0;
03872    int ignore_fail = 0;
03873    AST_DECLARE_APP_ARGS(args,
03874       AST_APP_ARG(queuename);
03875       AST_APP_ARG(interface);
03876       AST_APP_ARG(options);
03877    );
03878 
03879    if (ast_strlen_zero(data)) {
03880       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03881       return -1;
03882    }
03883 
03884    parse = ast_strdupa(data);
03885 
03886    AST_STANDARD_APP_ARGS(args, parse);
03887 
03888    lu = ast_module_user_add(chan);
03889 
03890    if (args.options) {
03891       if (strchr(args.options, 'j'))
03892          priority_jump = 1;
03893       if (strchr(args.options, 'i'))
03894          ignore_fail = 1;
03895    }
03896 
03897    if (ast_strlen_zero(args.interface)) {
03898       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03899       ast_module_user_remove(lu);
03900       return -1;
03901    }
03902 
03903    if (set_member_paused(args.queuename, args.interface, 0)) {
03904       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03905       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03906       if (priority_jump || ast_opt_priority_jumping) {
03907          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03908             ast_module_user_remove(lu);
03909             return 0;
03910          }
03911       }
03912       ast_module_user_remove(lu);
03913       if (ignore_fail) {
03914          return 0;
03915       } else {
03916          return -1;
03917       }
03918    }
03919 
03920    ast_module_user_remove(lu);
03921    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03922    return 0;
03923 }

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

Definition at line 1603 of file app_queue.c.

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

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

01604 {
01605    int digitlen = strlen(qe->digits);
01606 
01607    /* Prevent possible buffer overflow */
01608    if (digitlen < sizeof(qe->digits) - 2) {
01609       qe->digits[digitlen] = digit;
01610       qe->digits[digitlen + 1] = '\0';
01611    } else {
01612       qe->digits[0] = '\0';
01613       return 0;
01614    }
01615 
01616    /* If there's no context to goto, short-circuit */
01617    if (ast_strlen_zero(qe->context))
01618       return 0;
01619 
01620    /* If the extension is bad, then reset the digits to blank */
01621    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01622       qe->digits[0] = '\0';
01623       return 0;
01624    }
01625 
01626    /* We have an exact match */
01627    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01628       qe->valid_digits = 1;
01629       /* Return 1 on a successful goto */
01630       return 1;
01631    }
01632 
01633    return 0;
01634 }

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

Definition at line 1907 of file app_queue.c.

References ast_copy_string(), and pbx_builtin_serialize_variables().

Referenced by ring_entry().

01908 {
01909    char *tmp = alloca(len);
01910 
01911    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01912       int i, j;
01913 
01914       /* convert "\n" to "\nVariable: " */
01915       strcpy(vars, "Variable: ");
01916 
01917       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01918          vars[j] = tmp[i];
01919 
01920          if (tmp[i + 1] == '\0')
01921             break;
01922          if (tmp[i] == '\n') {
01923             vars[j++] = '\r';
01924             vars[j++] = '\n';
01925 
01926             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01927             j += 9;
01928          }
01929       }
01930       if (j > len - 3)
01931          j = len - 3;
01932       vars[j++] = '\r';
01933       vars[j++] = '\n';
01934       vars[j] = '\0';
01935    } else {
01936       /* there are no channel variables; leave it blank */
01937       *vars = '\0';
01938    }
01939    return vars;
01940 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3480 of file app_queue.c.

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

Referenced by queue_exec().

03481 {
03482    /* Don't need to hold the lock while we setup the outgoing calls */
03483    int retrywait = qe->parent->retry * 1000;
03484 
03485    int res = ast_waitfordigit(qe->chan, retrywait);
03486    if (res > 0 && !valid_exit(qe, res))
03487       res = 0;
03488 
03489    return res;
03490 }

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

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

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

02604 {
02605    int res = 0;
02606 
02607    /* This is the holding pen for callers 2 through maxlen */
02608    for (;;) {
02609       enum queue_member_status stat;
02610 
02611       if (is_our_turn(qe))
02612          break;
02613 
02614       /* If we have timed out, break out */
02615       if (qe->expire && (time(NULL) >= qe->expire)) {
02616          *reason = QUEUE_TIMEOUT;
02617          break;
02618       }
02619 
02620       stat = get_member_status(qe->parent, qe->max_penalty);
02621 
02622       /* leave the queue if no agents, if enabled */
02623       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02624          *reason = QUEUE_LEAVEEMPTY;
02625          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02626          leave_queue(qe);
02627          break;
02628       }
02629 
02630       /* leave the queue if no reachable agents, if enabled */
02631       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02632          *reason = QUEUE_LEAVEUNAVAIL;
02633          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02634          leave_queue(qe);
02635          break;
02636       }
02637 
02638       /* Make a position announcement, if enabled */
02639       if (qe->parent->announcefrequency && !ringing &&
02640          (res = say_position(qe)))
02641          break;
02642 
02643       /* If we have timed out, break out */
02644       if (qe->expire && (time(NULL) >= qe->expire)) {
02645          *reason = QUEUE_TIMEOUT;
02646          break;
02647       }
02648 
02649       /* Make a periodic announcement, if enabled */
02650       if (qe->parent->periodicannouncefrequency && !ringing &&
02651          (res = say_periodic_announcement(qe)))
02652          break;
02653 
02654       /* If we have timed out, break out */
02655       if (qe->expire && (time(NULL) >= qe->expire)) {
02656          *reason = QUEUE_TIMEOUT;
02657          break;
02658       }
02659       
02660       /* Wait a second before checking again */
02661       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02662          if (res > 0 && !valid_exit(qe, res))
02663             res = 0;
02664          else
02665             break;
02666       }
02667       
02668       /* If we have timed out, break out */
02669       if (qe->expire && (time(NULL) >= qe->expire)) {
02670          *reason = QUEUE_TIMEOUT;
02671          break;
02672       }
02673    }
02674 
02675    return res;
02676 }


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

char* app = "Queue" [static]

Definition at line 150 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 195 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 197 of file app_queue.c.

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

Definition at line 196 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 230 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 232 of file app_queue.c.

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

Definition at line 231 of file app_queue.c.

char* app_ql = "QueueLog" [static]

Definition at line 269 of file app_queue.c.

char* app_ql_descrip [static]

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

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

Definition at line 270 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 214 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 216 of file app_queue.c.

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

Definition at line 215 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 253 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 255 of file app_queue.c.

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

Definition at line 254 of file app_queue.c.

const struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 5671 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

Definition at line 291 of file app_queue.c.

struct ast_cli_entry cli_add_queue_member_deprecated [static]

Initial value:

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

Definition at line 5554 of file app_queue.c.

struct ast_cli_entry cli_queue[] [static]

Definition at line 5564 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 5559 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 5549 of file app_queue.c.

ast_cond_t cond

Condition for the state change queue

Definition at line 745 of file app_queue.c.

char* descrip [static]

Definition at line 154 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

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

struct statechange* first

Definition at line 747 of file app_queue.c.

enum queue_result id

Definition at line 307 of file app_queue.c.

Referenced by _sip_show_peers().

struct statechange* last

Definition at line 747 of file app_queue.c.

ast_mutex_t lock

Lock for the state change queue

Definition at line 743 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 294 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 277 of file app_queue.c.

char qam_cmd_usage[] [static]

Initial value:

"Usage: queue add member <channel> to <queue> [penalty <penalty> [as <membername> [state_interface <state_interface>]]]\n"

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

char qrm_cmd_usage[] [static]

Initial value:

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

Definition at line 5546 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 282 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

Definition at line 285 of file app_queue.c.

struct { ... } queue_results[]

Referenced by set_queue_result().

char queue_show_usage[] [static]

Initial value:

"Usage: queue show\n"
"       Provides summary information on a specified queue.\n"

Definition at line 5539 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 2773 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 4582 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4592 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4616 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 4607 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct { ... } state_change_q

Queue of state changes

unsigned int stop

Set to 1 to stop the thread

Definition at line 739 of file app_queue.c.

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

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

Definition at line 152 of file app_queue.c.

char* text

Definition at line 308 of file app_queue.c.

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

pthread_t thread

The device state monitoring thread

Definition at line 741 of file app_queue.c.

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 288 of file app_queue.c.


Generated on Sat Aug 6 00:39:36 2011 for Asterisk - the Open Source PBX by  doxygen 1.4.7