Tue Nov 4 13:20:24 2008

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  member
struct  member_interface
struct  queue_ent
struct  statechange
struct  strategy

Defines

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

Enumerations

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

Functions

static int __queues_show (struct mansession *s, int manager, int fd, int argc, char **argv)
static 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)
static struct call_queuealloc_queue (const char *queuename)
static int aqm_exec (struct ast_channel *chan, void *data)
static AST_LIST_HEAD_STATIC (queues, call_queue)
static AST_LIST_HEAD_STATIC (interfaces, member_interface)
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT,"True Call Queueing",.load=load_module,.unload=unload_module,.reload=reload,)
static int calc_metric (struct call_queue *q, struct member *mem, int pos, struct queue_ent *qe, struct callattempt *tmp)
 Calculate the metric of each member in the outgoing callattempts.
static void clear_and_free_interfaces (void)
static void clear_queue (struct call_queue *q)
static int compare_weight (struct call_queue *rq, struct member *member)
static char * complete_queue (const char *line, const char *word, int pos, int state)
static char * complete_queue_add_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_remove_member (const char *line, const char *word, int pos, int state)
static char * complete_queue_show (const char *line, const char *word, int pos, int state)
static int compress_char (const char c)
static struct membercreate_queue_member (const char *interface, const char *membername, int penalty, int paused)
 allocate space for new queue member and set fields based on parameters passed
static void destroy_queue (struct call_queue *q)
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_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 play_file (struct ast_channel *chan, char *filename)
static int pqm_exec (struct ast_channel *chan, void *data)
static int ql_exec (struct ast_channel *chan, void *data)
static int queue_exec (struct ast_channel *chan, void *data)
 The starting point for all queue calls.
static int queue_function_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 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 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 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)
 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)
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 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 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 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
   unsigned int   stop:1
   pthread_t   thread
device_state
 Data used by the device state thread.
static int montype_default = 0
 queues.conf [general] option
static const char * pm_family = "Queue/PersistentMembers"
 Persistent Members astdb family.
static char qam_cmd_usage []
static char qrm_cmd_usage []
static int queue_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_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 376 of file app_queue.c.

Referenced by queue_set_param().

#define ANNOUNCEHOLDTIME_ONCE   2

Definition at line 377 of file app_queue.c.

Referenced by queue_set_param(), and say_position().

#define AST_MAX_WATCHERS   256

Definition at line 2090 of file app_queue.c.

#define DEFAULT_RETRY   5

Definition at line 137 of file app_queue.c.

Referenced by init_queue(), and queue_set_param().

#define DEFAULT_TIMEOUT   15

Definition at line 138 of file app_queue.c.

Referenced by queue_set_param().

#define MAX_PERIODIC_ANNOUNCEMENTS   10

Definition at line 140 of file app_queue.c.

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

#define PM_MAX_LEN   8192

Definition at line 263 of file app_queue.c.

Referenced by dump_queue_members(), and reload_queue_members().

#define QUEUE_EMPTY_NORMAL   1

Definition at line 374 of file app_queue.c.

Referenced by queue_set_param().

#define QUEUE_EMPTY_STRICT   2

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

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

#define RECHECK   1

Definition at line 139 of file app_queue.c.

Referenced by wait_our_turn().

#define RES_EXISTS   (-1)

Definition at line 143 of file app_queue.c.

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

#define RES_NOSUCHQUEUE   (-3)

Definition at line 145 of file app_queue.c.

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

#define RES_NOT_DYNAMIC   (-4)

Definition at line 146 of file app_queue.c.

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

#define RES_OKAY   0

Definition at line 142 of file app_queue.c.

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

#define RES_OUTOFMEMORY   (-2)

Definition at line 144 of file app_queue.c.

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


Enumeration Type Documentation

anonymous enum

Enumerator:
QUEUE_STRATEGY_RINGALL 
QUEUE_STRATEGY_ROUNDROBIN 
QUEUE_STRATEGY_LEASTRECENT 
QUEUE_STRATEGY_FEWESTCALLS 
QUEUE_STRATEGY_RANDOM 
QUEUE_STRATEGY_RRMEMORY 

Definition at line 116 of file app_queue.c.

enum qmc_status

Enumerator:
QMC_VALID 
QMC_PAUSED 
QMC_ACTIVE 
QMC_FREE 
QMC_ALL 

Definition at line 3992 of file app_queue.c.

03992                 {
03993    QMC_VALID = 0, /* Count valid members */
03994    QMC_PAUSED,    /* Count paused members */
03995    QMC_ACTIVE,    /* Count active members */
03996    QMC_FREE,      /* Count free members */
03997    QMC_ALL        /* Count all queue members */
03998 };

enum queue_member_status

Enumerator:
QUEUE_NO_MEMBERS 
QUEUE_NO_REACHABLE_MEMBERS 
QUEUE_NORMAL 

Definition at line 527 of file app_queue.c.

00527                          {
00528    QUEUE_NO_MEMBERS,
00529    QUEUE_NO_REACHABLE_MEMBERS,
00530    QUEUE_NORMAL
00531 };

enum queue_result

Enumerator:
QUEUE_UNKNOWN 
QUEUE_TIMEOUT 
QUEUE_JOINEMPTY 
QUEUE_LEAVEEMPTY 
QUEUE_JOINUNAVAIL 
QUEUE_LEAVEUNAVAIL 
QUEUE_FULL 

Definition at line 280 of file app_queue.c.

00280                   {
00281    QUEUE_UNKNOWN = 0,
00282    QUEUE_TIMEOUT = 1,
00283    QUEUE_JOINEMPTY = 2,
00284    QUEUE_LEAVEEMPTY = 3,
00285    QUEUE_JOINUNAVAIL = 4,
00286    QUEUE_LEAVEUNAVAIL = 5,
00287    QUEUE_FULL = 6,
00288 };


Function Documentation

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

Definition at line 4409 of file app_queue.c.

References ao2_container_count(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ast_build_string(), ast_cli(), AST_LIST_EMPTY, AST_LIST_LOCK, AST_LIST_NEXT, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), 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::lastcall, load_realtime_queue(), call_queue::lock, call_queue::maxlen, member::membername, call_queue::members, call_queue::name, queue_ent::next, member::paused, member::penalty, queue_ent::prio, queue_show(), member::realtime, RESULT_SHOWUSAGE, RESULT_SUCCESS, call_queue::ringlimit, s, call_queue::servicelevel, queue_ent::start, member::status, call_queue::strategy, and call_queue::weight.

Referenced by manager_queues_show(), and queue_show().

04410 {
04411    struct call_queue *q;
04412    struct queue_ent *qe;
04413    struct member *mem;
04414    int pos, queue_show;
04415    time_t now;
04416    char max_buf[80];
04417    char *max;
04418    size_t max_left;
04419    float sl = 0;
04420    char *term = manager ? "\r\n" : "\n";
04421    struct ao2_iterator mem_iter;
04422 
04423    time(&now);
04424    if (argc == 2)
04425       queue_show = 0;
04426    else if (argc == 3)
04427       queue_show = 1;
04428    else
04429       return RESULT_SHOWUSAGE;
04430 
04431    /* We only want to load realtime queues when a specific queue is asked for. */
04432    if (queue_show)
04433       load_realtime_queue(argv[2]);
04434 
04435    AST_LIST_LOCK(&queues);
04436    if (AST_LIST_EMPTY(&queues)) {
04437       AST_LIST_UNLOCK(&queues);
04438       if (queue_show) {
04439          if (s)
04440             astman_append(s, "No such queue: %s.%s",argv[2], term);
04441          else
04442             ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04443       } else {
04444          if (s)
04445             astman_append(s, "No queues.%s", term);
04446          else
04447             ast_cli(fd, "No queues.%s", term);
04448       }
04449       return RESULT_SUCCESS;
04450    }
04451    AST_LIST_TRAVERSE(&queues, q, list) {
04452       ast_mutex_lock(&q->lock);
04453       if (queue_show) {
04454          if (strcasecmp(q->name, argv[2]) != 0) {
04455             ast_mutex_unlock(&q->lock);
04456             if (!AST_LIST_NEXT(q, list)) {
04457                ast_cli(fd, "No such queue: %s.%s",argv[2], term);
04458                break;
04459             }
04460             continue;
04461          }
04462       }
04463       max_buf[0] = '\0';
04464       max = max_buf;
04465       max_left = sizeof(max_buf);
04466       if (q->maxlen)
04467          ast_build_string(&max, &max_left, "%d", q->maxlen);
04468       else
04469          ast_build_string(&max, &max_left, "unlimited");
04470       sl = 0;
04471       if (q->callscompleted > 0)
04472          sl = 100 * ((float) q->callscompletedinsl / (float) q->callscompleted);
04473       if (s)
04474          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",
04475                               q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04476                               q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04477       else
04478          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",
04479                      q->name, q->count, max_buf, int2strat(q->strategy), q->holdtime, q->ringlimit,
04480                      q->weight, q->callscompleted, q->callsabandoned, sl, q->servicelevel, term);
04481       if (ao2_container_count(q->members)) {
04482          if (s)
04483             astman_append(s, "   Members: %s", term);
04484          else
04485             ast_cli(fd, "   Members: %s", term);
04486          mem_iter = ao2_iterator_init(q->members, 0);
04487          while ((mem = ao2_iterator_next(&mem_iter))) {
04488             max_buf[0] = '\0';
04489             max = max_buf;
04490             max_left = sizeof(max_buf);
04491             if (mem->penalty)
04492                ast_build_string(&max, &max_left, " with penalty %d", mem->penalty);
04493             if (mem->dynamic)
04494                ast_build_string(&max, &max_left, " (dynamic)");
04495             if (mem->realtime)
04496                ast_build_string(&max, &max_left, " (realtime)");
04497             if (mem->paused)
04498                ast_build_string(&max, &max_left, " (paused)");
04499             ast_build_string(&max, &max_left, " (%s)", devstate2str(mem->status));
04500             if (mem->calls) {
04501                ast_build_string(&max, &max_left, " has taken %d calls (last was %ld secs ago)",
04502                   mem->calls, (long) (time(NULL) - mem->lastcall));
04503             } else
04504                ast_build_string(&max, &max_left, " has taken no calls yet");
04505             if (s)
04506                astman_append(s, "      %s%s%s", mem->membername, max_buf, term);
04507             else
04508                ast_cli(fd, "      %s%s%s", mem->membername, max_buf, term);
04509             ao2_ref(mem, -1);
04510          }
04511       } else if (s)
04512          astman_append(s, "   No Members%s", term);
04513       else  
04514          ast_cli(fd, "   No Members%s", term);
04515       if (q->head) {
04516          pos = 1;
04517          if (s)
04518             astman_append(s, "   Callers: %s", term);
04519          else
04520             ast_cli(fd, "   Callers: %s", term);
04521          for (qe = q->head; qe; qe = qe->next) {
04522             if (s)
04523                astman_append(s, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s",
04524                   pos++, qe->chan->name, (long) (now - qe->start) / 60,
04525                   (long) (now - qe->start) % 60, qe->prio, term);
04526             else
04527                ast_cli(fd, "      %d. %s (wait: %ld:%2.2ld, prio: %d)%s", pos++,
04528                   qe->chan->name, (long) (now - qe->start) / 60,
04529                   (long) (now - qe->start) % 60, qe->prio, term);
04530          }
04531       } else if (s)
04532          astman_append(s, "   No Callers%s", term);
04533       else
04534          ast_cli(fd, "   No Callers%s", term);
04535       if (s)
04536          astman_append(s, "%s", term);
04537       else
04538          ast_cli(fd, "%s", term);
04539       ast_mutex_unlock(&q->lock);
04540       if (queue_show)
04541          break;
04542    }
04543    AST_LIST_UNLOCK(&queues);
04544    return RESULT_SUCCESS;
04545 }

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

Definition at line 869 of file app_queue.c.

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

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

00870 {
00871    struct member_interface *curint;
00872 
00873    AST_LIST_LOCK(&interfaces);
00874    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00875       if (!strcasecmp(curint->interface, interface))
00876          break;
00877    }
00878 
00879    if (curint) {
00880       AST_LIST_UNLOCK(&interfaces);
00881       return 0;
00882    }
00883 
00884    if (option_debug)
00885       ast_log(LOG_DEBUG, "Adding %s to the list of interfaces that make up all of our queue members.\n", interface);
00886    
00887    if ((curint = ast_calloc(1, sizeof(*curint)))) {
00888       ast_copy_string(curint->interface, interface, sizeof(curint->interface));
00889       AST_LIST_INSERT_HEAD(&interfaces, curint, list);
00890    }
00891    AST_LIST_UNLOCK(&interfaces);
00892 
00893    return 0;
00894 }

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

Definition at line 3242 of file app_queue.c.

References add_to_interfaces(), ao2_ref(), AST_LIST_LOCK, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::calls, create_queue_member(), dump_queue_members(), member::dynamic, EVENT_FLAG_AGENT, member::interface, interface_exists(), member::lastcall, load_realtime_queue(), call_queue::lock, 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, and member::status.

Referenced by aqm_exec(), handle_queue_add_member(), manager_add_queue_member(), and reload_queue_members().

03243 {
03244    struct call_queue *q;
03245    struct member *new_member, *old_member;
03246    int res = RES_NOSUCHQUEUE;
03247 
03248    /* \note Ensure the appropriate realtime queue is loaded.  Note that this
03249     * short-circuits if the queue is already in memory. */
03250    if (!(q = load_realtime_queue(queuename)))
03251       return res;
03252 
03253    AST_LIST_LOCK(&queues);
03254 
03255    ast_mutex_lock(&q->lock);
03256    if ((old_member = interface_exists(q, interface)) == NULL) {
03257       add_to_interfaces(interface);
03258       if ((new_member = create_queue_member(interface, membername, penalty, paused))) {
03259          new_member->dynamic = 1;
03260          ao2_link(q->members, new_member);
03261          q->membercount++;
03262          manager_event(EVENT_FLAG_AGENT, "QueueMemberAdded",
03263             "Queue: %s\r\n"
03264             "Location: %s\r\n"
03265             "MemberName: %s\r\n"
03266             "Membership: %s\r\n"
03267             "Penalty: %d\r\n"
03268             "CallsTaken: %d\r\n"
03269             "LastCall: %d\r\n"
03270             "Status: %d\r\n"
03271             "Paused: %d\r\n",
03272             q->name, new_member->interface, new_member->membername,
03273             "dynamic",
03274             new_member->penalty, new_member->calls, (int) new_member->lastcall,
03275             new_member->status, new_member->paused);
03276          
03277          ao2_ref(new_member, -1);
03278          new_member = NULL;
03279 
03280          if (dump)
03281             dump_queue_members(q);
03282          
03283          res = RES_OKAY;
03284       } else {
03285          res = RES_OUTOFMEMORY;
03286       }
03287    } else {
03288       ao2_ref(old_member, -1);
03289       res = RES_EXISTS;
03290    }
03291    ast_mutex_unlock(&q->lock);
03292    AST_LIST_UNLOCK(&queues);
03293 
03294    return res;
03295 }

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

Definition at line 770 of file app_queue.c.

References ast_calloc, and ast_mutex_init().

Referenced by find_queue_by_name_rt(), and reload_queues().

00771 {
00772    struct call_queue *q;
00773 
00774    if ((q = ast_calloc(1, sizeof(*q)))) {
00775       ast_mutex_init(&q->lock);
00776       ast_copy_string(q->name, queuename, sizeof(q->name));
00777    }
00778    return q;
00779 }

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

Definition at line 3621 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, parse(), pbx_builtin_setvar_helper(), RES_EXISTS, RES_NOSUCHQUEUE, RES_OKAY, and RES_OUTOFMEMORY.

Referenced by load_module().

03622 {
03623    int res=-1;
03624    struct ast_module_user *lu;
03625    char *parse, *temppos = NULL;
03626    int priority_jump = 0;
03627    AST_DECLARE_APP_ARGS(args,
03628       AST_APP_ARG(queuename);
03629       AST_APP_ARG(interface);
03630       AST_APP_ARG(penalty);
03631       AST_APP_ARG(options);
03632       AST_APP_ARG(membername);
03633    );
03634    int penalty = 0;
03635 
03636    if (ast_strlen_zero(data)) {
03637       ast_log(LOG_WARNING, "AddQueueMember requires an argument (queuename[|[interface]|[penalty][|options][|membername]])\n");
03638       return -1;
03639    }
03640 
03641    parse = ast_strdupa(data);
03642 
03643    AST_STANDARD_APP_ARGS(args, parse);
03644 
03645    lu = ast_module_user_add(chan);
03646 
03647    if (ast_strlen_zero(args.interface)) {
03648       args.interface = ast_strdupa(chan->name);
03649       temppos = strrchr(args.interface, '-');
03650       if (temppos)
03651          *temppos = '\0';
03652    }
03653 
03654    if (!ast_strlen_zero(args.penalty)) {
03655       if ((sscanf(args.penalty, "%d", &penalty) != 1) || penalty < 0) {
03656          ast_log(LOG_WARNING, "Penalty '%s' is invalid, must be an integer >= 0\n", args.penalty);
03657          penalty = 0;
03658       }
03659    }
03660    
03661    if (args.options) {
03662       if (strchr(args.options, 'j'))
03663          priority_jump = 1;
03664    }
03665 
03666    switch (add_to_queue(args.queuename, args.interface, args.membername, penalty, 0, queue_persistent_members)) {
03667    case RES_OKAY:
03668       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "ADDMEMBER", "%s", "");
03669       ast_log(LOG_NOTICE, "Added interface '%s' to queue '%s'\n", args.interface, args.queuename);
03670       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "ADDED");
03671       res = 0;
03672       break;
03673    case RES_EXISTS:
03674       ast_log(LOG_WARNING, "Unable to add interface '%s' to queue '%s': Already there\n", args.interface, args.queuename);
03675       if (priority_jump || ast_opt_priority_jumping)
03676          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03677       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "MEMBERALREADY");
03678       res = 0;
03679       break;
03680    case RES_NOSUCHQUEUE:
03681       ast_log(LOG_WARNING, "Unable to add interface to queue '%s': No such queue\n", args.queuename);
03682       pbx_builtin_setvar_helper(chan, "AQMSTATUS", "NOSUCHQUEUE");
03683       res = 0;
03684       break;
03685    case RES_OUTOFMEMORY:
03686       ast_log(LOG_ERROR, "Out of memory adding member %s to queue %s\n", args.interface, args.queuename);
03687       break;
03688    }
03689 
03690    ast_module_user_remove(lu);
03691 
03692    return res;
03693 }

static AST_LIST_HEAD_STATIC ( queues  ,
call_queue   
) [static]

static AST_LIST_HEAD_STATIC ( interfaces  ,
member_interface   
) [static]

AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_DEFAULT  ,
"True Call Queueing"  ,
load = load_module,
unload = unload_module,
reload = reload 
)

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

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

Referenced by try_calling().

02509 {
02510    if (qe->max_penalty && (mem->penalty > qe->max_penalty))
02511       return -1;
02512 
02513    switch (q->strategy) {
02514    case QUEUE_STRATEGY_RINGALL:
02515       /* Everyone equal, except for penalty */
02516       tmp->metric = mem->penalty * 1000000;
02517       break;
02518    case QUEUE_STRATEGY_ROUNDROBIN:
02519       if (!pos) {
02520          if (!q->wrapped) {
02521             /* No more channels, start over */
02522             q->rrpos = 0;
02523          } else {
02524             /* Prioritize next entry */
02525             q->rrpos++;
02526          }
02527          q->wrapped = 0;
02528       }
02529       /* Fall through */
02530    case QUEUE_STRATEGY_RRMEMORY:
02531       if (pos < q->rrpos) {
02532          tmp->metric = 1000 + pos;
02533       } else {
02534          if (pos > q->rrpos)
02535             /* Indicate there is another priority */
02536             q->wrapped = 1;
02537          tmp->metric = pos;
02538       }
02539       tmp->metric += mem->penalty * 1000000;
02540       break;
02541    case QUEUE_STRATEGY_RANDOM:
02542       tmp->metric = ast_random() % 1000;
02543       tmp->metric += mem->penalty * 1000000;
02544       break;
02545    case QUEUE_STRATEGY_FEWESTCALLS:
02546       tmp->metric = mem->calls;
02547       tmp->metric += mem->penalty * 1000000;
02548       break;
02549    case QUEUE_STRATEGY_LEASTRECENT:
02550       if (!mem->lastcall)
02551          tmp->metric = 0;
02552       else
02553          tmp->metric = 1000000 - (time(NULL) - mem->lastcall);
02554       tmp->metric += mem->penalty * 1000000;
02555       break;
02556    default:
02557       ast_log(LOG_WARNING, "Can't calculate metric for unknown strategy %d\n", q->strategy);
02558       break;
02559    }
02560    if (q->ringlimit && (mem->ringcount >= q->ringlimit)) {
02561       tmp->metric += (mem->ringcount / q->ringlimit) * 10000000;
02562    }
02563    if (option_debug)
02564       ast_log(LOG_DEBUG, "New metric %d for member %s with %d rings (limit %d)\n", 
02565                   tmp->metric, mem->interface, mem->ringcount, q->ringlimit);
02566    return 0;
02567 }

static void clear_and_free_interfaces ( void   )  [static]

Definition at line 943 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, and free.

Referenced by unload_module().

00944 {
00945    struct member_interface *curint;
00946 
00947    AST_LIST_LOCK(&interfaces);
00948    while ((curint = AST_LIST_REMOVE_HEAD(&interfaces, list)))
00949       free(curint);
00950    AST_LIST_UNLOCK(&interfaces);
00951 }

static void clear_queue ( struct call_queue q  )  [static]

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

00861 {
00862    q->holdtime = 0;
00863    q->callscompleted = 0;
00864    q->callsabandoned = 0;
00865    q->callscompletedinsl = 0;
00866    q->wrapuptime = 0;
00867 }

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

Definition at line 1717 of file app_queue.c.

References ao2_find(), ao2_ref(), AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::count, member::interface, call_queue::lock, LOG_DEBUG, call_queue::members, call_queue::name, and call_queue::weight.

Referenced by ring_entry().

01718 {
01719    struct call_queue *q;
01720    struct member *mem;
01721    int found = 0;
01722    
01723    /* &qlock and &rq->lock already set by try_calling()
01724     * to solve deadlock */
01725    AST_LIST_TRAVERSE(&queues, q, list) {
01726       if (q == rq) /* don't check myself, could deadlock */
01727          continue;
01728       ast_mutex_lock(&q->lock);
01729       if (q->count && q->members) {
01730          if ((mem = ao2_find(q->members, member, OBJ_POINTER))) {
01731             ast_log(LOG_DEBUG, "Found matching member %s in queue '%s'\n", mem->interface, q->name);
01732             if (q->weight > rq->weight) {
01733                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);
01734                found = 1;
01735             }
01736             ao2_ref(mem, -1);
01737          }
01738       }
01739       ast_mutex_unlock(&q->lock);
01740       if (found)
01741          break;
01742    }
01743    return found;
01744 }

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

Definition at line 4552 of file app_queue.c.

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

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

04553 {
04554    struct call_queue *q;
04555    char *ret = NULL;
04556    int which = 0;
04557    int wordlen = strlen(word);
04558    
04559    AST_LIST_LOCK(&queues);
04560    AST_LIST_TRAVERSE(&queues, q, list) {
04561       if (!strncasecmp(word, q->name, wordlen) && ++which > state) {
04562          ret = ast_strdup(q->name); 
04563          break;
04564       }
04565    }
04566    AST_LIST_UNLOCK(&queues);
04567 
04568    return ret;
04569 }

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

Definition at line 4847 of file app_queue.c.

References ast_malloc, ast_strdup, and complete_queue().

04848 {
04849    /* 0 - queue; 1 - add; 2 - member; 3 - <interface>; 4 - to; 5 - <queue>; 6 - penalty; 7 - <penalty>; 8 - as; 9 - <membername> */
04850    switch (pos) {
04851    case 3:  /* Don't attempt to complete name of interface (infinite possibilities) */
04852       return NULL;
04853    case 4:  /* only one possible match, "to" */
04854       return state == 0 ? ast_strdup("to") : NULL;
04855    case 5:  /* <queue> */
04856       return complete_queue(line, word, pos, state);
04857    case 6: /* only one possible match, "penalty" */
04858       return state == 0 ? ast_strdup("penalty") : NULL;
04859    case 7:
04860       if (state < 100) {   /* 0-99 */
04861          char *num;
04862          if ((num = ast_malloc(3))) {
04863             sprintf(num, "%d", state);
04864          }
04865          return num;
04866       } else {
04867          return NULL;
04868       }
04869    case 8: /* only one possible match, "as" */
04870       return state == 0 ? ast_strdup("as") : NULL;
04871    case 9:  /* Don't attempt to complete name of member (infinite possibilities) */
04872       return NULL;
04873    default:
04874       return NULL;
04875    }
04876 }

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

Definition at line 4913 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_EMPTY, AST_LIST_TRAVERSE, ast_mutex_lock(), ast_mutex_unlock(), ast_strdup, complete_queue(), member::interface, call_queue::lock, and call_queue::members.

04914 {
04915    int which = 0;
04916    struct call_queue *q;
04917    struct member *m;
04918    struct ao2_iterator mem_iter;
04919 
04920    /* 0 - queue; 1 - remove; 2 - member; 3 - <member>; 4 - from; 5 - <queue> */
04921    if (pos > 5 || pos < 3)
04922       return NULL;
04923    if (pos == 4)  /* only one possible match, 'from' */
04924       return state == 0 ? ast_strdup("from") : NULL;
04925 
04926    if (pos == 5)  /* No need to duplicate code */
04927       return complete_queue(line, word, pos, state);
04928 
04929    /* here is the case for 3, <member> */
04930    if (!AST_LIST_EMPTY(&queues)) { /* XXX unnecessary ? the traverse does that for us */
04931       AST_LIST_TRAVERSE(&queues, q, list) {
04932          ast_mutex_lock(&q->lock);
04933          mem_iter = ao2_iterator_init(q->members, 0);
04934          while ((m = ao2_iterator_next(&mem_iter))) {
04935             if (++which > state) {
04936                char *tmp;
04937                ast_mutex_unlock(&q->lock);
04938                tmp = ast_strdup(m->interface);
04939                ao2_ref(m, -1);
04940                return tmp;
04941             }
04942             ao2_ref(m, -1);
04943          }
04944          ast_mutex_unlock(&q->lock);
04945       }
04946    }
04947 
04948    return NULL;
04949 }

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

Definition at line 4571 of file app_queue.c.

References complete_queue().

04572 {
04573    if (pos == 2)
04574       return complete_queue(line, word, pos, state);
04575    return NULL;
04576 }

static int compress_char ( const char  c  )  [static]

Definition at line 781 of file app_queue.c.

00782 {
00783    if (c < 32)
00784       return 0;
00785    else if (c > 96)
00786       return c - 64;
00787    else
00788       return c - 32;
00789 }

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

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

Definition at line 750 of file app_queue.c.

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

00751 {
00752    struct member *cur;
00753    
00754    if ((cur = ao2_alloc(sizeof(*cur), NULL))) {
00755       cur->penalty = penalty;
00756       cur->paused = paused;
00757       ast_copy_string(cur->interface, interface, sizeof(cur->interface));
00758       if (!ast_strlen_zero(membername))
00759          ast_copy_string(cur->membername, membername, sizeof(cur->membername));
00760       else
00761          ast_copy_string(cur->membername, interface, sizeof(cur->membername));
00762       if (!strchr(cur->interface, '/'))
00763          ast_log(LOG_WARNING, "No location at interface '%s'\n", interface);
00764       cur->status = ast_device_state(interface);
00765    }
00766 
00767    return cur;
00768 }

static void destroy_queue ( struct call_queue q  )  [static]

Definition at line 1170 of file app_queue.c.

References ao2_ref(), ast_mutex_destroy(), free, free_members(), call_queue::lock, and call_queue::members.

Referenced by find_queue_by_name_rt(), and leave_queue().

01171 {
01172    free_members(q, 1);
01173    ast_mutex_destroy(&q->lock);
01174    ao2_ref(q->members, -1);
01175    free(q);
01176 }

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

Consumer of the statechange queue.

Definition at line 698 of file app_queue.c.

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

Referenced by load_module().

00699 {
00700    struct statechange *sc = NULL;
00701 
00702    while (!device_state.stop) {
00703       ast_mutex_lock(&device_state.lock);
00704       if (!(sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry))) {
00705          ast_cond_wait(&device_state.cond, &device_state.lock);
00706          sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry);
00707       }
00708       ast_mutex_unlock(&device_state.lock);
00709 
00710       /* Check to see if we were woken up to see the request to stop */
00711       if (device_state.stop)
00712          break;
00713 
00714       if (!sc)
00715          continue;
00716 
00717       handle_statechange(sc);
00718 
00719       free(sc);
00720       sc = NULL;
00721    }
00722 
00723    if (sc)
00724       free(sc);
00725 
00726    while ((sc = AST_LIST_REMOVE_HEAD(&device_state.state_change_q, entry)))
00727       free(sc);
00728 
00729    return NULL;
00730 }

static void do_hang ( struct callattempt o  )  [static]

common hangup actions

Definition at line 1747 of file app_queue.c.

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

Referenced by ring_entry().

01748 {
01749    o->stillgoing = 0;
01750    ast_hangup(o->chan);
01751    o->chan = NULL;
01752 }

static void dump_queue_members ( struct call_queue pm_queue  )  [static]

Definition at line 3149 of file app_queue.c.

References 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, and PM_MAX_LEN.

Referenced by add_to_queue(), remove_from_queue(), and set_member_paused().

03150 {
03151    struct member *cur_member;
03152    char value[PM_MAX_LEN];
03153    int value_len = 0;
03154    int res;
03155    struct ao2_iterator mem_iter;
03156 
03157    memset(value, 0, sizeof(value));
03158 
03159    if (!pm_queue)
03160       return;
03161 
03162    mem_iter = ao2_iterator_init(pm_queue->members, 0);
03163    while ((cur_member = ao2_iterator_next(&mem_iter))) {
03164       if (!cur_member->dynamic) {
03165          ao2_ref(cur_member, -1);
03166          continue;
03167       }
03168 
03169       res = snprintf(value + value_len, sizeof(value) - value_len, "%s%s;%d;%d;%s",
03170          value_len ? "|" : "", cur_member->interface, cur_member->penalty, cur_member->paused, cur_member->membername);
03171 
03172       ao2_ref(cur_member, -1);
03173 
03174       if (res != strlen(value + value_len)) {
03175          ast_log(LOG_WARNING, "Could not create persistent member string, out of space\n");
03176          break;
03177       }
03178       value_len += res;
03179    }
03180    
03181    if (value_len && !cur_member) {
03182       if (ast_db_put(pm_family, pm_queue->name, value))
03183          ast_log(LOG_WARNING, "failed to create persistent dynamic entry!\n");
03184    } else
03185       /* Delete the entry if the queue is empty or there is an error */
03186       ast_db_del(pm_family, pm_queue->name);
03187 }

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

find the entry with the best metric, or NULL

Definition at line 1936 of file app_queue.c.

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

01937 {
01938    struct callattempt *best = NULL, *cur;
01939 
01940    for (cur = outgoing; cur; cur = cur->q_next) {
01941       if (cur->stillgoing &&              /* Not already done */
01942          !cur->chan &&              /* Isn't already going */
01943          (!best || cur->metric < best->metric)) {     /* We haven't found one yet, or it's better */
01944          best = cur;
01945       }
01946    }
01947 
01948    return best;
01949 }

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

References alloc_queue(), AST_LIST_INSERT_HEAD, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), clear_queue(), call_queue::dead, destroy_queue(), init_queue(), member_interface::interface, call_queue::lock, LOG_DEBUG, LOG_WARNING, ast_variable::name, call_queue::name, ast_variable::next, queue_set_param(), call_queue::realtime, and ast_variable::value.

Referenced by load_realtime_queue().

01182 {
01183    struct ast_variable *v;
01184    struct call_queue *q;
01185    struct member *m;
01186    struct ao2_iterator mem_iter;
01187    char *interface = NULL;
01188    char *tmp, *tmp_name;
01189    char tmpbuf[64];  /* Must be longer than the longest queue param name. */
01190 
01191    /* Find the queue in the in-core list (we will create a new one if not found). */
01192    AST_LIST_TRAVERSE(&queues, q, list) {
01193       if (!strcasecmp(q->name, queuename))
01194          break;
01195    }
01196 
01197    /* Static queues override realtime. */
01198    if (q) {
01199       ast_mutex_lock(&q->lock);
01200       if (!q->realtime) {
01201          if (q->dead) {
01202             ast_mutex_unlock(&q->lock);
01203             return NULL;
01204          } else {
01205             ast_log(LOG_WARNING, "Static queue '%s' already exists. Not loading from realtime\n", q->name);
01206             ast_mutex_unlock(&q->lock);
01207             return q;
01208          }
01209       }
01210    } else if (!member_config)
01211       /* Not found in the list, and it's not realtime ... */
01212       return NULL;
01213 
01214    /* Check if queue is defined in realtime. */
01215    if (!queue_vars) {
01216       /* Delete queue from in-core list if it has been deleted in realtime. */
01217       if (q) {
01218          /*! \note Hmm, can't seem to distinguish a DB failure from a not
01219             found condition... So we might delete an in-core queue
01220             in case of DB failure. */
01221          ast_log(LOG_DEBUG, "Queue %s not found in realtime.\n", queuename);
01222 
01223          q->dead = 1;
01224          /* Delete if unused (else will be deleted when last caller leaves). */
01225          if (!q->count) {
01226             /* Delete. */
01227             AST_LIST_REMOVE(&queues, q, list);
01228             ast_mutex_unlock(&q->lock);
01229             destroy_queue(q);
01230          } else
01231             ast_mutex_unlock(&q->lock);
01232       }
01233       return NULL;
01234    }
01235 
01236    /* Create a new queue if an in-core entry does not exist yet. */
01237    if (!q) {
01238       if (!(q = alloc_queue(queuename)))
01239          return NULL;
01240       ast_mutex_lock(&q->lock);
01241       clear_queue(q);
01242       q->realtime = 1;
01243       init_queue(q);    /* Ensure defaults for all parameters not set explicitly. */
01244       AST_LIST_INSERT_HEAD(&queues, q, list);
01245    }
01246 
01247    memset(tmpbuf, 0, sizeof(tmpbuf));
01248    for (v = queue_vars; v; v = v->next) {
01249       /* Convert to dashes `-' from underscores `_' as the latter are more SQL friendly. */
01250       if ((tmp = strchr(v->name, '_'))) {
01251          ast_copy_string(tmpbuf, v->name, sizeof(tmpbuf));
01252          tmp_name = tmpbuf;
01253          tmp = tmp_name;
01254          while ((tmp = strchr(tmp, '_')))
01255             *tmp++ = '-';
01256       } else
01257          tmp_name = v->name;
01258 
01259       if (!ast_strlen_zero(v->value)) {
01260          /* Don't want to try to set the option if the value is empty */
01261          queue_set_param(q, tmp_name, v->value, -1, 0);
01262       }
01263    }
01264 
01265    if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
01266       rr_dep_warning();
01267 
01268    /* Temporarily set realtime members dead so we can detect deleted ones. 
01269     * Also set the membercount correctly for realtime*/
01270    mem_iter = ao2_iterator_init(q->members, 0);
01271    while ((m = ao2_iterator_next(&mem_iter))) {
01272       q->membercount++;
01273       if (m->realtime)
01274          m->dead = 1;
01275       ao2_ref(m, -1);
01276    }
01277 
01278    while ((interface = ast_category_browse(member_config, interface))) {
01279       rt_handle_member_record(q, interface,
01280          ast_variable_retrieve(member_config, interface, "membername"),
01281          ast_variable_retrieve(member_config, interface, "penalty"),
01282          ast_variable_retrieve(member_config, interface, "paused"));
01283    }
01284 
01285    /* Delete all realtime members that have been deleted in DB. */
01286    mem_iter = ao2_iterator_init(q->members, 0);
01287    while ((m = ao2_iterator_next(&mem_iter))) {
01288       if (m->dead) {
01289          ao2_unlink(q->members, m);
01290          ast_mutex_unlock(&q->lock);
01291          remove_from_interfaces(m->interface);
01292          ast_mutex_lock(&q->lock);
01293          q->membercount--;
01294       }
01295       ao2_ref(m, -1);
01296    }
01297 
01298    ast_mutex_unlock(&q->lock);
01299 
01300    return q;
01301 }

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

Definition at line 1154 of file app_queue.c.

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

Referenced by destroy_queue().

01155 {
01156    /* Free non-dynamic members */
01157    struct member *cur;
01158    struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
01159 
01160    while ((cur = ao2_iterator_next(&mem_iter))) {
01161       if (all || !cur->dynamic) {
01162          ao2_unlink(q->members, cur);
01163          remove_from_interfaces(cur->interface);
01164          q->membercount--;
01165       }
01166       ao2_ref(cur, -1);
01167    }
01168 }

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

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INVALID, AST_DEVICE_UNAVAILABLE, ast_mutex_lock(), ast_mutex_unlock(), call_queue::lock, 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().

00540 {
00541    struct member *member;
00542    struct ao2_iterator mem_iter;
00543    enum queue_member_status result = QUEUE_NO_MEMBERS;
00544 
00545    ast_mutex_lock(&q->lock);
00546    mem_iter = ao2_iterator_init(q->members, 0);
00547    while ((member = ao2_iterator_next(&mem_iter))) {
00548       if (max_penalty && (member->penalty > max_penalty)) {
00549          ao2_ref(member, -1);
00550          continue;
00551       }
00552 
00553       if (member->paused) {
00554          ao2_ref(member, -1);
00555          continue;
00556       }
00557 
00558       switch (member->status) {
00559       case AST_DEVICE_INVALID:
00560          /* nothing to do */
00561          ao2_ref(member, -1);
00562          break;
00563       case AST_DEVICE_UNAVAILABLE:
00564          result = QUEUE_NO_REACHABLE_MEMBERS;
00565          ao2_ref(member, -1);
00566          break;
00567       default:
00568          ast_mutex_unlock(&q->lock);
00569          ao2_ref(member, -1);
00570          return QUEUE_NORMAL;
00571       }
00572    }
00573 
00574    ast_mutex_unlock(&q->lock);
00575    return result;
00576 }

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

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

04794 {
04795    char *queuename, *interface, *membername = NULL;
04796    int penalty;
04797 
04798    if ((argc != 6) && (argc != 8) && (argc != 10)) {
04799       return RESULT_SHOWUSAGE;
04800    } else if (strcmp(argv[4], "to")) {
04801       return RESULT_SHOWUSAGE;
04802    } else if ((argc == 8) && strcmp(argv[6], "penalty")) {
04803       return RESULT_SHOWUSAGE;
04804    } else if ((argc == 10) && strcmp(argv[8], "as")) {
04805       return RESULT_SHOWUSAGE;
04806    }
04807 
04808    queuename = argv[5];
04809    interface = argv[3];
04810    if (argc >= 8) {
04811       if (sscanf(argv[7], "%d", &penalty) == 1) {
04812          if (penalty < 0) {
04813             ast_cli(fd, "Penalty must be >= 0\n");
04814             penalty = 0;
04815          }
04816       } else {
04817          ast_cli(fd, "Penalty must be an integer >= 0\n");
04818          penalty = 0;
04819       }
04820    } else {
04821       penalty = 0;
04822    }
04823 
04824    if (argc >= 10) {
04825       membername = argv[9];
04826    }
04827 
04828    switch (add_to_queue(queuename, interface, membername, penalty, 0, queue_persistent_members)) {
04829    case RES_OKAY:
04830       ast_queue_log(queuename, "CLI", interface, "ADDMEMBER", "%s", "");
04831       ast_cli(fd, "Added interface '%s' to queue '%s'\n", interface, queuename);
04832       return RESULT_SUCCESS;
04833    case RES_EXISTS:
04834       ast_cli(fd, "Unable to add interface '%s' to queue '%s': Already there\n", interface, queuename);
04835       return RESULT_FAILURE;
04836    case RES_NOSUCHQUEUE:
04837       ast_cli(fd, "Unable to add interface to queue '%s': No such queue\n", queuename);
04838       return RESULT_FAILURE;
04839    case RES_OUTOFMEMORY:
04840       ast_cli(fd, "Out of memory\n");
04841       return RESULT_FAILURE;
04842    default:
04843       return RESULT_FAILURE;
04844    }
04845 }

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

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

04879 {
04880    char *queuename, *interface;
04881 
04882    if (argc != 6) {
04883       return RESULT_SHOWUSAGE;
04884    } else if (strcmp(argv[4], "from")) {
04885       return RESULT_SHOWUSAGE;
04886    }
04887 
04888    queuename = argv[5];
04889    interface = argv[3];
04890 
04891    switch (remove_from_queue(queuename, interface)) {
04892    case RES_OKAY:
04893       ast_queue_log(queuename, "CLI", interface, "REMOVEMEMBER", "%s", "");
04894       ast_cli(fd, "Removed interface '%s' from queue '%s'\n", interface, queuename);
04895       return RESULT_SUCCESS;
04896    case RES_EXISTS:
04897       ast_cli(fd, "Unable to remove interface '%s' from queue '%s': Not there\n", interface, queuename);
04898       return RESULT_FAILURE;
04899    case RES_NOSUCHQUEUE:
04900       ast_cli(fd, "Unable to remove interface from queue '%s': No such queue\n", queuename);
04901       return RESULT_FAILURE;
04902    case RES_OUTOFMEMORY:
04903       ast_cli(fd, "Out of memory\n");
04904       return RESULT_FAILURE;
04905    case RES_NOT_DYNAMIC:
04906       ast_cli(fd, "Member not dynamic\n");
04907       return RESULT_FAILURE;
04908    default:
04909       return RESULT_FAILURE;
04910    }
04911 }

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

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_strdupa, devstate2str(), member_interface::interface, LOG_DEBUG, option_debug, and update_status().

Referenced by device_state_thread().

00638 {
00639    struct member_interface *curint;
00640    char *loc;
00641    char *technology;
00642 
00643    technology = ast_strdupa(sc->dev);
00644    loc = strchr(technology, '/');
00645    if (loc) {
00646       *loc++ = '\0';
00647    } else {
00648       return NULL;
00649    }
00650 
00651    AST_LIST_LOCK(&interfaces);
00652    AST_LIST_TRAVERSE(&interfaces, curint, list) {
00653       char *interface;
00654       char *slash_pos;
00655       interface = ast_strdupa(curint->interface);
00656       if ((slash_pos = strchr(interface, '/')))
00657          if ((slash_pos = strchr(slash_pos + 1, '/')))
00658             *slash_pos = '\0';
00659 
00660       if (!strcasecmp(interface, sc->dev))
00661          break;
00662    }
00663    AST_LIST_UNLOCK(&interfaces);
00664 
00665    if (!curint) {
00666       if (option_debug > 2)
00667          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));
00668       return NULL;
00669    }
00670 
00671    if (option_debug)
00672       ast_log(LOG_DEBUG, "Device '%s/%s' changed to state '%d' (%s)\n", technology, loc, sc->state, devstate2str(sc->state));
00673 
00674    update_status(sc->dev, sc->state);
00675 
00676    return NULL;
00677 }

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

Definition at line 1698 of file app_queue.c.

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

Referenced by try_calling().

01699 {
01700    struct callattempt *oo;
01701 
01702    while (outgoing) {
01703       /* Hangup any existing lines we have open */
01704       if (outgoing->chan && (outgoing->chan != exception))
01705          ast_hangup(outgoing->chan);
01706       oo = outgoing;
01707       outgoing = outgoing->q_next;
01708       if (oo->member)
01709          ao2_ref(oo->member, -1);
01710       free(oo);
01711    }
01712 }

static void init_queue ( struct call_queue q  )  [static]

Definition at line 809 of file app_queue.c.

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

Referenced by find_queue_by_name_rt(), and reload_queues().

00810 {
00811    int i;
00812 
00813    q->dead = 0;
00814    q->retry = DEFAULT_RETRY;
00815    q->timeout = -1;
00816    q->maxlen = 0;
00817    q->ringlimit = 0;
00818    q->announcefrequency = 0;
00819    q->announceholdtime = 0;
00820    q->roundingseconds = 0; /* Default - don't announce seconds */
00821    q->servicelevel = 0;
00822    q->ringinuse = 1;
00823    q->setinterfacevar = 0;
00824    q->autofill = autofill_default;
00825    q->montype = montype_default;
00826    q->moh[0] = '\0';
00827    q->announce[0] = '\0';
00828    q->context[0] = '\0';
00829    q->monfmt[0] = '\0';
00830    q->periodicannouncefrequency = 0;
00831    q->reportholdtime = 0;
00832    q->monjoin = 0;
00833    q->wrapuptime = 0;
00834    q->joinempty = 0;
00835    q->leavewhenempty = 0;
00836    q->memberdelay = 0;
00837    q->maskmemberstatus = 0;
00838    q->eventwhencalled = 0;
00839    q->weight = 0;
00840    q->timeoutrestart = 0;
00841    if (!q->members)
00842       q->members = ao2_container_alloc(37, member_hash_fn, member_cmp_fn);
00843    q->membercount = 0;
00844    q->found = 1;
00845    ast_copy_string(q->sound_next, "queue-youarenext", sizeof(q->sound_next));
00846    ast_copy_string(q->sound_thereare, "queue-thereare", sizeof(q->sound_thereare));
00847    ast_copy_string(q->sound_calls, "queue-callswaiting", sizeof(q->sound_calls));
00848    ast_copy_string(q->sound_holdtime, "queue-holdtime", sizeof(q->sound_holdtime));
00849    ast_copy_string(q->sound_minutes, "queue-minutes", sizeof(q->sound_minutes));
00850    ast_copy_string(q->sound_seconds, "queue-seconds", sizeof(q->sound_seconds));
00851    ast_copy_string(q->sound_thanks, "queue-thankyou", sizeof(q->sound_thanks));
00852    ast_copy_string(q->sound_lessthan, "queue-less-than", sizeof(q->sound_lessthan));
00853    ast_copy_string(q->sound_reporthold, "queue-reporthold", sizeof(q->sound_reporthold));
00854    ast_copy_string(q->sound_periodicannounce[0], "queue-periodic-announce", sizeof(q->sound_periodicannounce[0]));
00855    for (i = 1; i < MAX_PERIODIC_ANNOUNCEMENTS; i++) {
00856       q->sound_periodicannounce[i][0]='\0';
00857    }
00858 }

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

References call_queue::head, queue_ent::next, and queue_ent::parent.

Referenced by join_queue().

00509 {
00510    struct queue_ent *cur;
00511 
00512    if (!q || !new)
00513       return;
00514    if (prev) {
00515       cur = prev->next;
00516       prev->next = new;
00517    } else {
00518       cur = q->head;
00519       q->head = new;
00520    }
00521    new->next = cur;
00522    new->parent = q;
00523    new->pos = ++(*pos);
00524    new->opos = *pos;
00525 }

static char* int2strat ( int  strategy  )  [static]

Definition at line 483 of file app_queue.c.

References name, and strategies.

Referenced by __queues_show().

00484 {
00485    int x;
00486 
00487    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00488       if (strategy == strategies[x].strategy)
00489          return strategies[x].name;
00490    }
00491 
00492    return "<unknown>";
00493 }

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

Definition at line 3125 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), member::interface, and call_queue::members.

Referenced by add_to_queue(), and set_member_paused().

03126 {
03127    struct member *mem;
03128    struct ao2_iterator mem_iter;
03129 
03130    if (!q)
03131       return NULL;
03132 
03133    mem_iter = ao2_iterator_init(q->members, 0);
03134    while ((mem = ao2_iterator_next(&mem_iter))) {
03135       if (!strcasecmp(interface, mem->interface))
03136          return mem;
03137       ao2_ref(mem, -1);
03138    }
03139 
03140    return NULL;
03141 }

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

Definition at line 896 of file app_queue.c.

References ao2_find(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), member::interface, call_queue::lock, and call_queue::members.

Referenced by remove_from_interfaces().

00897 {
00898    struct call_queue *q;
00899    struct member *mem, tmpmem;
00900    int ret = 0;
00901 
00902    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
00903 
00904    AST_LIST_LOCK(&queues);
00905    AST_LIST_TRAVERSE(&queues, q, list) {
00906       ast_mutex_lock(&q->lock);
00907       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
00908          ao2_ref(mem, -1);
00909          ret = 1;
00910       }
00911       ast_mutex_unlock(&q->lock);
00912       if (ret)
00913          break;
00914    }
00915    AST_LIST_UNLOCK(&queues);
00916 
00917    return ret;
00918 }

static int is_our_turn ( struct queue_ent qe  )  [static]

Check if we should start attempting to call queue members.

The behavior of this function is dependent first on whether autofill is enabled and second on whether the ring strategy is ringall. If autofill is not enabled, then return true if we're the head of the queue. If autofill is enabled, then we count the available members and see if the number of available members is enough that given our position in the queue, we would theoretically be able to connect to one of those available members

Definition at line 2348 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_DEVICE_INUSE, AST_DEVICE_NOT_INUSE, AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), call_queue::autofill, queue_ent::chan, call_queue::head, call_queue::lock, LOG_DEBUG, call_queue::members, queue_ent::next, option_debug, queue_ent::parent, member::paused, queue_ent::pending, QUEUE_STRATEGY_RINGALL, call_queue::ringinuse, member::status, and call_queue::strategy.

Referenced by queue_exec(), and wait_our_turn().

02349 {
02350    struct queue_ent *ch;
02351    struct member *cur;
02352    int avl = 0;
02353    int idx = 0;
02354    int res;
02355 
02356    if (!qe->parent->autofill) {
02357       /* Atomically read the parent head -- does not need a lock */
02358       ch = qe->parent->head;
02359       /* If we are now at the top of the head, break out */
02360       if (ch == qe) {
02361          if (option_debug)
02362             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02363          res = 1;
02364       } else {
02365          if (option_debug)
02366             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02367          res = 0;
02368       }  
02369 
02370    } else {
02371       /* This needs a lock. How many members are available to be served? */
02372       ast_mutex_lock(&qe->parent->lock);
02373          
02374       ch = qe->parent->head;
02375    
02376       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
02377          if (option_debug)
02378             ast_log(LOG_DEBUG, "Even though there may be multiple members available, the strategy is ringall so only the head call is allowed in\n");
02379          avl = 1;
02380       } else {
02381          struct ao2_iterator mem_iter = ao2_iterator_init(qe->parent->members, 0);
02382          while ((cur = ao2_iterator_next(&mem_iter))) {
02383             switch (cur->status) {
02384             case AST_DEVICE_INUSE:
02385                if (!qe->parent->ringinuse)
02386                   break;
02387                /* else fall through */
02388             case AST_DEVICE_NOT_INUSE:
02389             case AST_DEVICE_UNKNOWN:
02390                if (!cur->paused)
02391                   avl++;
02392                break;
02393             }
02394             ao2_ref(cur, -1);
02395          }
02396       }
02397 
02398       if (option_debug)
02399          ast_log(LOG_DEBUG, "There are %d available members.\n", avl);
02400    
02401       while ((idx < avl) && (ch) && (ch != qe)) {
02402          if (!ch->pending)
02403             idx++;
02404          ch = ch->next;       
02405       }
02406    
02407       /* If the queue entry is within avl [the number of available members] calls from the top ... */
02408       if (ch && idx < avl) {
02409          if (option_debug)
02410             ast_log(LOG_DEBUG, "It's our turn (%s).\n", qe->chan->name);
02411          res = 1;
02412       } else {
02413          if (option_debug)
02414             ast_log(LOG_DEBUG, "It's not our turn (%s).\n", qe->chan->name);
02415          res = 0;
02416       }
02417       
02418       ast_mutex_unlock(&qe->parent->lock);
02419    }
02420 
02421    return res;
02422 }

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

Definition at line 1419 of file app_queue.c.

References queue_ent::announce, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), 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(), call_queue::lock, LOG_DEBUG, manager_event(), queue_ent::max_penalty, call_queue::maxlen, queue_ent::moh, queue_ent::next, option_debug, queue_ent::pos, queue_ent::prio, QUEUE_EMPTY_STRICT, QUEUE_FULL, QUEUE_JOINEMPTY, QUEUE_JOINUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, and S_OR.

Referenced by queue_exec().

01420 {
01421    struct call_queue *q;
01422    struct queue_ent *cur, *prev = NULL;
01423    int res = -1;
01424    int pos = 0;
01425    int inserted = 0;
01426    enum queue_member_status stat;
01427 
01428    if (!(q = load_realtime_queue(queuename)))
01429       return res;
01430 
01431    AST_LIST_LOCK(&queues);
01432    ast_mutex_lock(&q->lock);
01433 
01434    /* This is our one */
01435    stat = get_member_status(q, qe->max_penalty);
01436    if (!q->joinempty && (stat == QUEUE_NO_MEMBERS))
01437       *reason = QUEUE_JOINEMPTY;
01438    else if ((q->joinempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS || stat == QUEUE_NO_MEMBERS))
01439       *reason = QUEUE_JOINUNAVAIL;
01440    else if (q->maxlen && (q->count >= q->maxlen))
01441       *reason = QUEUE_FULL;
01442    else {
01443       /* There's space for us, put us at the right position inside
01444        * the queue.
01445        * Take into account the priority of the calling user */
01446       inserted = 0;
01447       prev = NULL;
01448       cur = q->head;
01449       while (cur) {
01450          /* We have higher priority than the current user, enter
01451           * before him, after all the other users with priority
01452           * higher or equal to our priority. */
01453          if ((!inserted) && (qe->prio > cur->prio)) {
01454             insert_entry(q, prev, qe, &pos);
01455             inserted = 1;
01456          }
01457          cur->pos = ++pos;
01458          prev = cur;
01459          cur = cur->next;
01460       }
01461       /* No luck, join at the end of the queue */
01462       if (!inserted)
01463          insert_entry(q, prev, qe, &pos);
01464       ast_copy_string(qe->moh, q->moh, sizeof(qe->moh));
01465       ast_copy_string(qe->announce, q->announce, sizeof(qe->announce));
01466       ast_copy_string(qe->context, q->context, sizeof(qe->context));
01467       q->count++;
01468       res = 0;
01469       manager_event(EVENT_FLAG_CALL, "Join",
01470          "Channel: %s\r\nCallerID: %s\r\nCallerIDName: %s\r\nQueue: %s\r\nPosition: %d\r\nCount: %d\r\nUniqueid: %s\r\n",
01471          qe->chan->name,
01472          S_OR(qe->chan->cid.cid_num, "unknown"), /* XXX somewhere else it is <unknown> */
01473          S_OR(qe->chan->cid.cid_name, "unknown"),
01474          q->name, qe->pos, q->count, qe->chan->uniqueid );
01475       if (option_debug)
01476          ast_log(LOG_DEBUG, "Queue '%s' Join, Channel '%s', Position '%d'\n", q->name, qe->chan->name, qe->pos );
01477    }
01478    ast_mutex_unlock(&q->lock);
01479    AST_LIST_UNLOCK(&queues);
01480 
01481    return res;
01482 }

static void leave_queue ( struct queue_ent qe  )  [static]

Definition at line 1654 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), queue_ent::chan, call_queue::count, call_queue::dead, destroy_queue(), EVENT_FLAG_CALL, call_queue::head, call_queue::lock, LOG_DEBUG, manager_event(), call_queue::name, queue_ent::next, option_debug, queue_ent::parent, and queue_ent::pos.

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

01655 {
01656    struct call_queue *q;
01657    struct queue_ent *cur, *prev = NULL;
01658    int pos = 0;
01659 
01660    if (!(q = qe->parent))
01661       return;
01662    ast_mutex_lock(&q->lock);
01663 
01664    prev = NULL;
01665    for (cur = q->head; cur; cur = cur->next) {
01666       if (cur == qe) {
01667          q->count--;
01668 
01669          /* Take us out of the queue */
01670          manager_event(EVENT_FLAG_CALL, "Leave",
01671             "Channel: %s\r\nQueue: %s\r\nCount: %d\r\nUniqueid: %s\r\n",
01672             qe->chan->name, q->name,  q->count, qe->chan->uniqueid);
01673          if (option_debug)
01674             ast_log(LOG_DEBUG, "Queue '%s' Leave, Channel '%s'\n", q->name, qe->chan->name );
01675          /* Take us out of the queue */
01676          if (prev)
01677             prev->next = cur->next;
01678          else
01679             q->head = cur->next;
01680       } else {
01681          /* Renumber the people after us in the queue based on a new count */
01682          cur->pos = ++pos;
01683          prev = cur;
01684       }
01685    }
01686    ast_mutex_unlock(&q->lock);
01687 
01688    if (q->dead && !q->count) {   
01689       /* It's dead and nobody is in it, so kill it */
01690       AST_LIST_LOCK(&queues);
01691       AST_LIST_REMOVE(&queues, q, list);
01692       AST_LIST_UNLOCK(&queues);
01693       destroy_queue(q);
01694    }
01695 }

static int load_module ( void   )  [static]

Definition at line 5033 of file app_queue.c.

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

05034 {
05035    int res;
05036 
05037    if (!reload_queues())
05038       return AST_MODULE_LOAD_DECLINE;
05039 
05040    if (queue_persistent_members)
05041       reload_queue_members();
05042 
05043    ast_mutex_init(&device_state.lock);
05044    ast_cond_init(&device_state.cond, NULL);
05045    ast_pthread_create(&device_state.thread, NULL, device_state_thread, NULL);
05046 
05047    ast_cli_register_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05048    res = ast_register_application(app, queue_exec, synopsis, descrip);
05049    res |= ast_register_application(app_aqm, aqm_exec, app_aqm_synopsis, app_aqm_descrip);
05050    res |= ast_register_application(app_rqm, rqm_exec, app_rqm_synopsis, app_rqm_descrip);
05051    res |= ast_register_application(app_pqm, pqm_exec, app_pqm_synopsis, app_pqm_descrip);
05052    res |= ast_register_application(app_upqm, upqm_exec, app_upqm_synopsis, app_upqm_descrip);
05053    res |= ast_register_application(app_ql, ql_exec, app_ql_synopsis, app_ql_descrip);
05054    res |= ast_manager_register("Queues", 0, manager_queues_show, "Queues");
05055    res |= ast_manager_register("QueueStatus", 0, manager_queues_status, "Queue Status");
05056    res |= ast_manager_register("QueueAdd", EVENT_FLAG_AGENT, manager_add_queue_member, "Add interface to queue.");
05057    res |= ast_manager_register("QueueRemove", EVENT_FLAG_AGENT, manager_remove_queue_member, "Remove interface from queue.");
05058    res |= ast_manager_register("QueuePause", EVENT_FLAG_AGENT, manager_pause_queue_member, "Makes a queue member temporarily unavailable");
05059    res |= ast_custom_function_register(&queueagentcount_function);
05060    res |= ast_custom_function_register(&queuemembercount_function);
05061    res |= ast_custom_function_register(&queuememberlist_function);
05062    res |= ast_custom_function_register(&queuewaitingcount_function);
05063    res |= ast_devstate_add(statechange_queue, NULL);
05064 
05065    return res;
05066 }

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

Definition at line 1369 of file app_queue.c.

References ast_config_destroy(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_load_realtime_multientry(), ast_log(), ast_variables_destroy(), find_queue_by_name_rt(), LOG_ERROR, call_queue::name, call_queue::realtime, and update_realtime_members().

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

01370 {
01371    struct ast_variable *queue_vars;
01372    struct ast_config *member_config = NULL;
01373    struct call_queue *q;
01374 
01375    /* Find the queue in the in-core list first. */
01376    AST_LIST_LOCK(&queues);
01377    AST_LIST_TRAVERSE(&queues, q, list) {
01378       if (!strcasecmp(q->name, queuename)) {
01379          break;
01380       }
01381    }
01382    AST_LIST_UNLOCK(&queues);
01383 
01384    if (!q || q->realtime) {
01385       /*! \note Load from realtime before taking the global qlock, to avoid blocking all
01386          queue operations while waiting for the DB.
01387 
01388          This will be two separate database transactions, so we might
01389          see queue parameters as they were before another process
01390          changed the queue and member list as it was after the change.
01391          Thus we might see an empty member list when a queue is
01392          deleted. In practise, this is unlikely to cause a problem. */
01393 
01394       queue_vars = ast_load_realtime("queues", "name", queuename, NULL);
01395       if (queue_vars) {
01396          member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", queuename, NULL);
01397          if (!member_config) {
01398             ast_log(LOG_ERROR, "no queue_members defined in your config (extconfig.conf).\n");
01399             ast_variables_destroy(queue_vars);
01400             return NULL;
01401          }
01402       }
01403 
01404       AST_LIST_LOCK(&queues);
01405 
01406       q = find_queue_by_name_rt(queuename, queue_vars, member_config);
01407       if (member_config)
01408          ast_config_destroy(member_config);
01409       if (queue_vars)
01410          ast_variables_destroy(queue_vars);
01411 
01412       AST_LIST_UNLOCK(&queues);
01413    } else { 
01414       update_realtime_members(q);
01415    }
01416    return q;
01417 }

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

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

04687 {
04688    const char *queuename, *interface, *penalty_s, *paused_s, *membername;
04689    int paused, penalty = 0;
04690 
04691    queuename = astman_get_header(m, "Queue");
04692    interface = astman_get_header(m, "Interface");
04693    penalty_s = astman_get_header(m, "Penalty");
04694    paused_s = astman_get_header(m, "Paused");
04695    membername = astman_get_header(m, "MemberName");
04696 
04697    if (ast_strlen_zero(queuename)) {
04698       astman_send_error(s, m, "'Queue' not specified.");
04699       return 0;
04700    }
04701 
04702    if (ast_strlen_zero(interface)) {
04703       astman_send_error(s, m, "'Interface' not specified.");
04704       return 0;
04705    }
04706 
04707    if (ast_strlen_zero(penalty_s))
04708       penalty = 0;
04709    else if (sscanf(penalty_s, "%d", &penalty) != 1 || penalty < 0)
04710       penalty = 0;
04711 
04712    if (ast_strlen_zero(paused_s))
04713       paused = 0;
04714    else
04715       paused = abs(ast_true(paused_s));
04716 
04717    switch (add_to_queue(queuename, interface, membername, penalty, paused, queue_persistent_members)) {
04718    case RES_OKAY:
04719       ast_queue_log(queuename, "MANAGER", interface, "ADDMEMBER", "%s", "");
04720       astman_send_ack(s, m, "Added interface to queue");
04721       break;
04722    case RES_EXISTS:
04723       astman_send_error(s, m, "Unable to add interface: Already there");
04724       break;
04725    case RES_NOSUCHQUEUE:
04726       astman_send_error(s, m, "Unable to add interface to queue: No such queue");
04727       break;
04728    case RES_OUTOFMEMORY:
04729       astman_send_error(s, m, "Out of memory");
04730       break;
04731    }
04732 
04733    return 0;
04734 }

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

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

04771 {
04772    const char *queuename, *interface, *paused_s;
04773    int paused;
04774 
04775    interface = astman_get_header(m, "Interface");
04776    paused_s = astman_get_header(m, "Paused");
04777    queuename = astman_get_header(m, "Queue");   /* Optional - if not supplied, pause the given Interface in all queues */
04778 
04779    if (ast_strlen_zero(interface) || ast_strlen_zero(paused_s)) {
04780       astman_send_error(s, m, "Need 'Interface' and 'Paused' parameters.");
04781       return 0;
04782    }
04783 
04784    paused = abs(ast_true(paused_s));
04785 
04786    if (set_member_paused(queuename, interface, paused))
04787       astman_send_error(s, m, "Interface not found");
04788    else
04789       astman_send_ack(s, m, paused ? "Interface paused successfully" : "Interface unpaused successfully");
04790    return 0;
04791 }

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

Definition at line 4581 of file app_queue.c.

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

Referenced by load_module().

04582 {
04583    char *a[] = { "queue", "show" };
04584 
04585    __queues_show(s, 1, -1, 2, a);
04586    astman_append(s, "\r\n\r\n"); /* Properly terminate Manager output */
04587 
04588    return RESULT_SUCCESS;
04589 }

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

Definition at line 4592 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_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::lock, call_queue::maxlen, member::membername, call_queue::members, 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().

04593 {
04594    time_t now;
04595    int pos;
04596    const char *id = astman_get_header(m,"ActionID");
04597    const char *queuefilter = astman_get_header(m,"Queue");
04598    const char *memberfilter = astman_get_header(m,"Member");
04599    char idText[256] = "";
04600    struct call_queue *q;
04601    struct queue_ent *qe;
04602    float sl = 0;
04603    struct member *mem;
04604    struct ao2_iterator mem_iter;
04605 
04606    astman_send_ack(s, m, "Queue status will follow");
04607    time(&now);
04608    AST_LIST_LOCK(&queues);
04609    if (!ast_strlen_zero(id))
04610       snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
04611 
04612    AST_LIST_TRAVERSE(&queues, q, list) {
04613       ast_mutex_lock(&q->lock);
04614 
04615       /* List queue properties */
04616       if (ast_strlen_zero(queuefilter) || !strcmp(q->name, queuefilter)) {
04617          sl = ((q->callscompleted > 0) ? 100 * ((float)q->callscompletedinsl / (float)q->callscompleted) : 0);
04618          astman_append(s, "Event: QueueParams\r\n"
04619             "Queue: %s\r\n"
04620             "Max: %d\r\n"
04621             "Calls: %d\r\n"
04622             "Holdtime: %d\r\n"
04623             "Completed: %d\r\n"
04624             "Abandoned: %d\r\n"
04625             "ServiceLevel: %d\r\n"
04626             "ServicelevelPerf: %2.1f\r\n"
04627             "RingLimit: %d\r\n"
04628             "Weight: %d\r\n"
04629             "%s"
04630             "\r\n",
04631             q->name, q->maxlen, q->count, q->holdtime, q->callscompleted,
04632             q->callsabandoned, q->servicelevel, sl,  q->ringlimit, q->weight, idText);
04633          /* List Queue Members */
04634          mem_iter = ao2_iterator_init(q->members, 0);
04635          while ((mem = ao2_iterator_next(&mem_iter))) {
04636             if (ast_strlen_zero(memberfilter) || !strcmp(mem->interface, memberfilter)) {
04637                astman_append(s, "Event: QueueMember\r\n"
04638                   "Queue: %s\r\n"
04639                   "Name: %s\r\n"
04640                   "Location: %s\r\n"
04641                   "Membership: %s\r\n"
04642                   "Penalty: %d\r\n"
04643                   "CallsTaken: %d\r\n"
04644                   "LastCall: %d\r\n"
04645                   "Status: %d\r\n"
04646                   "Paused: %d\r\n"
04647                   "%s"
04648                   "\r\n",
04649                   q->name, mem->membername, mem->interface, mem->dynamic ? "dynamic" : "static",
04650                   mem->penalty, mem->calls, (int)mem->lastcall, mem->status, mem->paused, idText);
04651             }
04652             ao2_ref(mem, -1);
04653          }
04654          /* List Queue Entries */
04655          pos = 1;
04656          for (qe = q->head; qe; qe = qe->next) {
04657             astman_append(s, "Event: QueueEntry\r\n"
04658                "Queue: %s\r\n"
04659                "Position: %d\r\n"
04660                "Channel: %s\r\n"
04661                "CallerID: %s\r\n"
04662                "CallerIDName: %s\r\n"
04663                "Wait: %ld\r\n"
04664                "%s"
04665                "\r\n",
04666                q->name, pos++, qe->chan->name,
04667                S_OR(qe->chan->cid.cid_num, "unknown"),
04668                S_OR(qe->chan->cid.cid_name, "unknown"),
04669                (long) (now - qe->start), idText);
04670          }
04671       }
04672       ast_mutex_unlock(&q->lock);
04673    }
04674 
04675    astman_append(s,
04676       "Event: QueueStatusComplete\r\n"
04677       "%s"
04678       "\r\n",idText);
04679 
04680    AST_LIST_UNLOCK(&queues);
04681 
04682 
04683    return RESULT_SUCCESS;
04684 }

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

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

04737 {
04738    const char *queuename, *interface;
04739 
04740    queuename = astman_get_header(m, "Queue");
04741    interface = astman_get_header(m, "Interface");
04742 
04743    if (ast_strlen_zero(queuename) || ast_strlen_zero(interface)) {
04744       astman_send_error(s, m, "Need 'Queue' and 'Interface' parameters.");
04745       return 0;
04746    }
04747 
04748    switch (remove_from_queue(queuename, interface)) {
04749    case RES_OKAY:
04750       ast_queue_log(queuename, "MANAGER", interface, "REMOVEMEMBER", "%s", "");
04751       astman_send_ack(s, m, "Removed interface from queue");
04752       break;
04753    case RES_EXISTS:
04754       astman_send_error(s, m, "Unable to remove interface: Not there");
04755       break;
04756    case RES_NOSUCHQUEUE:
04757       astman_send_error(s, m, "Unable to remove interface from queue: No such queue");
04758       break;
04759    case RES_OUTOFMEMORY:
04760       astman_send_error(s, m, "Out of memory");
04761       break;
04762    case RES_NOT_DYNAMIC:
04763       astman_send_error(s, m, "Member not dynamic");
04764       break;
04765    }
04766 
04767    return 0;
04768 }

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

Definition at line 803 of file app_queue.c.

References member::interface.

Referenced by init_queue().

00804 {
00805    struct member *mem1 = obj1, *mem2 = obj2;
00806    return strcmp(mem1->interface, mem2->interface) ? 0 : CMP_MATCH;
00807 }

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

Definition at line 791 of file app_queue.c.

References compress_char(), and member::interface.

Referenced by init_queue().

00792 {
00793    const struct member *mem = obj;
00794    const char *chname = strchr(mem->interface, '/');
00795    int ret = 0, i;
00796    if (!chname)
00797       chname = mem->interface;
00798    for (i = 0; i < 5 && chname[i]; i++)
00799       ret += compress_char(chname[i]) << (i * 6);
00800    return ret;
00801 }

static void monjoin_dep_warning ( void   )  [static]

Definition at line 462 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by queue_set_param().

00463 {
00464    static unsigned int warned = 0;
00465    if (!warned) {
00466       ast_log(LOG_NOTICE, "The 'monitor-join' queue option is deprecated. Please use monitor-type=mixmonitor instead.\n");
00467       warned = 1;
00468    }
00469 }

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

Definition at line 1484 of file app_queue.c.

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

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

01485 {
01486    int res;
01487 
01488    ast_stopstream(chan);
01489 
01490    res = ast_streamfile(chan, filename, chan->language);
01491    if (!res)
01492       res = ast_waitstream(chan, AST_DIGIT_ANY);
01493 
01494    ast_stopstream(chan);
01495 
01496    return res;
01497 }

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

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

03439 {
03440    struct ast_module_user *lu;
03441    char *parse;
03442    int priority_jump = 0;
03443    int ignore_fail = 0;
03444    AST_DECLARE_APP_ARGS(args,
03445       AST_APP_ARG(queuename);
03446       AST_APP_ARG(interface);
03447       AST_APP_ARG(options);
03448    );
03449 
03450    if (ast_strlen_zero(data)) {
03451       ast_log(LOG_WARNING, "PauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03452       return -1;
03453    }
03454 
03455    parse = ast_strdupa(data);
03456 
03457    AST_STANDARD_APP_ARGS(args, parse);
03458 
03459    lu = ast_module_user_add(chan);
03460 
03461    if (args.options) {
03462       if (strchr(args.options, 'j'))
03463          priority_jump = 1;
03464       if (strchr(args.options, 'i'))
03465          ignore_fail = 1;
03466    }
03467 
03468    if (ast_strlen_zero(args.interface)) {
03469       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03470       ast_module_user_remove(lu);
03471       return -1;
03472    }
03473 
03474    if (set_member_paused(args.queuename, args.interface, 1)) {
03475       ast_log(LOG_WARNING, "Attempt to pause interface %s, not found\n", args.interface);
03476       pbx_builtin_setvar_helper(chan, "PQMSTATUS", "NOTFOUND");
03477       if (priority_jump || ast_opt_priority_jumping) {
03478          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03479             ast_module_user_remove(lu);
03480             return 0;
03481          }
03482       }
03483       ast_module_user_remove(lu);
03484       if (ignore_fail) {
03485          return 0;
03486       } else {
03487          return -1;
03488       }
03489    }
03490 
03491    ast_module_user_remove(lu);
03492    pbx_builtin_setvar_helper(chan, "PQMSTATUS", "PAUSED");
03493    return 0;
03494 }

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

Definition at line 3695 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, event, LOG_WARNING, and parse().

Referenced by load_module().

03696 {
03697    struct ast_module_user *u;
03698    char *parse;
03699 
03700    AST_DECLARE_APP_ARGS(args,
03701       AST_APP_ARG(queuename);
03702       AST_APP_ARG(uniqueid);
03703       AST_APP_ARG(membername);
03704       AST_APP_ARG(event);
03705       AST_APP_ARG(params);
03706    );
03707 
03708    if (ast_strlen_zero(data)) {
03709       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo]\n");
03710       return -1;
03711    }
03712 
03713    u = ast_module_user_add(chan);
03714 
03715    parse = ast_strdupa(data);
03716 
03717    AST_STANDARD_APP_ARGS(args, parse);
03718 
03719    if (ast_strlen_zero(args.queuename) || ast_strlen_zero(args.uniqueid)
03720        || ast_strlen_zero(args.membername) || ast_strlen_zero(args.event)) {
03721       ast_log(LOG_WARNING, "QueueLog requires arguments (queuename|uniqueid|membername|event[|additionalinfo])\n");
03722       ast_module_user_remove(u);
03723       return -1;
03724    }
03725 
03726    ast_queue_log(args.queuename, args.uniqueid, args.membername, args.event, 
03727       "%s", args.params ? args.params : "");
03728 
03729    ast_module_user_remove(u);
03730 
03731    return 0;
03732 }

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

References 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_PBX_KEEPALIVE, 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, get_member_status(), is_our_turn(), join_queue(), leave_queue(), LOG_DEBUG, LOG_WARNING, option_debug, option_verbose, parse(), pbx_builtin_getvar_helper(), QUEUE_EMPTY_STRICT, QUEUE_LEAVEEMPTY, QUEUE_LEAVEUNAVAIL, QUEUE_NO_MEMBERS, QUEUE_NO_REACHABLE_MEMBERS, QUEUE_TIMEOUT, QUEUE_UNKNOWN, record_abandoned(), S_OR, say_periodic_announcement(), say_position(), set_queue_result(), stop, try_calling(), update_realtime_members(), VERBOSE_PREFIX_3, wait_a_bit(), and wait_our_turn().

Referenced by load_module().

03747 {
03748    int res=-1;
03749    int ringing=0;
03750    struct ast_module_user *lu;
03751    const char *user_priority;
03752    const char *max_penalty_str;
03753    int prio;
03754    int max_penalty;
03755    enum queue_result reason = QUEUE_UNKNOWN;
03756    /* whether to exit Queue application after the timeout hits */
03757    int tries = 0;
03758    int noption = 0;
03759    char *parse;
03760    AST_DECLARE_APP_ARGS(args,
03761       AST_APP_ARG(queuename);
03762       AST_APP_ARG(options);
03763       AST_APP_ARG(url);
03764       AST_APP_ARG(announceoverride);
03765       AST_APP_ARG(queuetimeoutstr);
03766       AST_APP_ARG(agi);
03767    );
03768    /* Our queue entry */
03769    struct queue_ent qe;
03770    
03771    if (ast_strlen_zero(data)) {
03772       ast_log(LOG_WARNING, "Queue requires an argument: queuename[|options[|URL[|announceoverride[|timeout[|agi]]]]]\n");
03773       return -1;
03774    }
03775    
03776    parse = ast_strdupa(data);
03777    AST_STANDARD_APP_ARGS(args, parse);
03778 
03779    lu = ast_module_user_add(chan);
03780 
03781    /* Setup our queue entry */
03782    memset(&qe, 0, sizeof(qe));
03783    qe.start = time(NULL);
03784 
03785    /* set the expire time based on the supplied timeout; */
03786    if (!ast_strlen_zero(args.queuetimeoutstr))
03787       qe.expire = qe.start + atoi(args.queuetimeoutstr);
03788    else
03789       qe.expire = 0;
03790 
03791    /* Get the priority from the variable ${QUEUE_PRIO} */
03792    user_priority = pbx_builtin_getvar_helper(chan, "QUEUE_PRIO");
03793    if (user_priority) {
03794       if (sscanf(user_priority, "%d", &prio) == 1) {
03795          if (option_debug)
03796             ast_log(LOG_DEBUG, "%s: Got priority %d from ${QUEUE_PRIO}.\n",
03797                chan->name, prio);
03798       } else {
03799          ast_log(LOG_WARNING, "${QUEUE_PRIO}: Invalid value (%s), channel %s.\n",
03800             user_priority, chan->name);
03801          prio = 0;
03802       }
03803    } else {
03804       if (option_debug > 2)
03805          ast_log(LOG_DEBUG, "NO QUEUE_PRIO variable found. Using default.\n");
03806       prio = 0;
03807    }
03808 
03809    /* Get the maximum penalty from the variable ${QUEUE_MAX_PENALTY} */
03810    if ((max_penalty_str = pbx_builtin_getvar_helper(chan, "QUEUE_MAX_PENALTY"))) {
03811       if (sscanf(max_penalty_str, "%d", &max_penalty) == 1) {
03812          if (option_debug)
03813             ast_log(LOG_DEBUG, "%s: Got max penalty %d from ${QUEUE_MAX_PENALTY}.\n",
03814                chan->name, max_penalty);
03815       } else {
03816          ast_log(LOG_WARNING, "${QUEUE_MAX_PENALTY}: Invalid value (%s), channel %s.\n",
03817             max_penalty_str, chan->name);
03818          max_penalty = 0;
03819       }
03820    } else {
03821       max_penalty = 0;
03822    }
03823 
03824    if (args.options && (strchr(args.options, 'r')))
03825       ringing = 1;
03826 
03827    if (option_debug)
03828       ast_log(LOG_DEBUG, "queue: %s, options: %s, url: %s, announce: %s, expires: %ld, priority: %d\n",
03829          args.queuename, args.options, args.url, args.announceoverride, (long)qe.expire, prio);
03830 
03831    qe.chan = chan;
03832    qe.prio = prio;
03833    qe.max_penalty = max_penalty;
03834    qe.last_pos_said = 0;
03835    qe.last_pos = 0;
03836    qe.last_periodic_announce_time = time(NULL);
03837    qe.last_periodic_announce_sound = 0;
03838    qe.valid_digits = 0;
03839    if (!join_queue(args.queuename, &qe, &reason)) {
03840       int makeannouncement = 0;
03841 
03842       ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ENTERQUEUE", "%s|%s", S_OR(args.url, ""),
03843          S_OR(chan->cid.cid_num, ""));
03844 check_turns:
03845       if (ringing) {
03846          ast_indicate(chan, AST_CONTROL_RINGING);
03847       } else {
03848          ast_moh_start(chan, qe.moh, NULL);
03849       }
03850 
03851       /* This is the wait loop for callers 2 through maxlen */
03852       res = wait_our_turn(&qe, ringing, &reason);
03853       if (res)
03854          goto stop;
03855 
03856       for (;;) {
03857          /* This is the wait loop for the head caller*/
03858          /* To exit, they may get their call answered; */
03859          /* they may dial a digit from the queue context; */
03860          /* or, they may timeout. */
03861 
03862          enum queue_member_status stat;
03863 
03864          /* Leave if we have exceeded our queuetimeout */
03865          if (qe.expire && (time(NULL) > qe.expire)) {
03866             record_abandoned(&qe);
03867             reason = QUEUE_TIMEOUT;
03868             res = 0;
03869             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03870             break;
03871          }
03872 
03873          if (makeannouncement) {
03874             /* Make a position announcement, if enabled */
03875             if (qe.parent->announcefrequency && !ringing)
03876                if ((res = say_position(&qe)))
03877                   goto stop;
03878 
03879          }
03880          makeannouncement = 1;
03881 
03882          /* Make a periodic announcement, if enabled */
03883          if (qe.parent->periodicannouncefrequency && !ringing)
03884             if ((res = say_periodic_announcement(&qe)))
03885                goto stop;
03886 
03887          /* Try calling all queue members for 'timeout' seconds */
03888          res = try_calling(&qe, args.options, args.announceoverride, args.url, &tries, &noption, args.agi);
03889          if (res)
03890             goto stop;
03891 
03892          stat = get_member_status(qe.parent, qe.max_penalty);
03893 
03894          /* exit after 'timeout' cycle if 'n' option enabled */
03895          if (noption && tries >= qe.parent->membercount) {
03896             if (option_verbose > 2)
03897                ast_verbose(VERBOSE_PREFIX_3 "Exiting on time-out cycle\n");
03898             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03899             record_abandoned(&qe);
03900             reason = QUEUE_TIMEOUT;
03901             res = 0;
03902             break;
03903          }
03904 
03905          /* leave the queue if no agents, if enabled */
03906          if (qe.parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
03907             record_abandoned(&qe);
03908             reason = QUEUE_LEAVEEMPTY;
03909             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03910             res = 0;
03911             break;
03912          }
03913 
03914          /* leave the queue if no reachable agents, if enabled */
03915          if ((qe.parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
03916             record_abandoned(&qe);
03917             reason = QUEUE_LEAVEUNAVAIL;
03918             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe.pos, qe.opos, (long)(time(NULL) - qe.start));
03919             res = 0;
03920             break;
03921          }
03922 
03923          /* Leave if we have exceeded our queuetimeout */
03924          if (qe.expire && (time(NULL) > qe.expire)) {
03925             record_abandoned(&qe);
03926             reason = QUEUE_TIMEOUT;
03927             res = 0;
03928             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHTIMEOUT", "%d", qe.pos);
03929             break;
03930          }
03931 
03932          /* If using dynamic realtime members, we should regenerate the member list for this queue */
03933          update_realtime_members(qe.parent);
03934 
03935          /* OK, we didn't get anybody; wait for 'retry' seconds; may get a digit to exit with */
03936          res = wait_a_bit(&qe);
03937          if (res)
03938             goto stop;
03939 
03940 
03941          /* Since this is a priority queue and
03942           * it is not sure that we are still at the head
03943           * of the queue, go and check for our turn again.
03944           */
03945          if (!is_our_turn(&qe)) {
03946             if (option_debug)
03947                ast_log(LOG_DEBUG, "Darn priorities, going back in queue (%s)!\n",
03948                   qe.chan->name);
03949             goto check_turns;
03950          }
03951       }
03952 
03953 stop:
03954       if (res) {
03955          if (res < 0) {
03956             if (!qe.handled) {
03957                record_abandoned(&qe);
03958                ast_queue_log(args.queuename, chan->uniqueid, "NONE", "ABANDON",
03959                   "%d|%d|%ld", qe.pos, qe.opos,
03960                   (long) time(NULL) - qe.start);
03961             }
03962             res = -1;
03963          } else if (qe.valid_digits) {
03964             ast_queue_log(args.queuename, chan->uniqueid, "NONE", "EXITWITHKEY",
03965                "%s|%d", qe.digits, qe.pos);
03966          }
03967       }
03968 
03969       /* Don't allow return code > 0 */
03970       if (res >= 0 && res != AST_PBX_KEEPALIVE) {
03971          res = 0; 
03972          if (ringing) {
03973             ast_indicate(chan, -1);
03974          } else {
03975             ast_moh_stop(chan);
03976          }        
03977          ast_stopstream(chan);
03978       }
03979       leave_queue(&qe);
03980       if (reason != QUEUE_UNKNOWN)
03981          set_queue_result(chan, reason);
03982    } else {
03983       ast_log(LOG_WARNING, "Unable to join queue '%s'\n", args.queuename);
03984       set_queue_result(chan, reason);
03985       res = 0;
03986    }
03987    ast_module_user_remove(lu);
03988 
03989    return res;
03990 }

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

Definition at line 4000 of file app_queue.c.

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

04001 {
04002    int count = 0;
04003    struct call_queue *q;
04004    struct ast_module_user *lu;
04005    struct member *m;
04006    struct ao2_iterator mem_iter;
04007    char *name, *item;
04008    enum qmc_status mode = QMC_VALID;
04009 
04010    buf[0] = '\0';
04011    
04012    if (ast_strlen_zero(data)) {
04013       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04014       return -1;
04015    }
04016 
04017    name = ast_strdupa(data);
04018  
04019    lu = ast_module_user_add(chan);
04020 
04021    if ((item = strchr(name, ':'))) {
04022       *item = '\0';
04023       item++;
04024    } else {
04025       item = "";
04026    }
04027 
04028    if (!strcasecmp(item, "valid")) {
04029       mode = QMC_VALID;
04030    } else  if (!strcasecmp(item, "paused")) {
04031       mode = QMC_PAUSED;
04032    } else  if (!strcasecmp(item, "active")) {
04033       mode = QMC_ACTIVE;
04034    } else  if (!strcasecmp(item, "free")) {
04035       mode = QMC_FREE;
04036    } else  if (!strcasecmp(item, "all")) {
04037       mode = QMC_ALL;
04038    }
04039 
04040    if ((q = load_realtime_queue(name))) {
04041       ast_mutex_lock(&q->lock);
04042       mem_iter = ao2_iterator_init(q->members, 0);
04043       while ((m = ao2_iterator_next(&mem_iter))) {
04044          switch (mode) {
04045          case QMC_VALID:
04046             /* Count the queue members who are logged in and presently answering calls */
04047             if ((m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04048                count++;
04049             }
04050             break;
04051          case QMC_PAUSED:
04052             /* Count paused members */
04053             if (m->paused) {
04054                count++;
04055             }
04056             break;
04057          case QMC_ACTIVE:
04058             /* Count not paused members who are logged in and presently answering calls */
04059             if (!m->paused && (m->status != AST_DEVICE_UNAVAILABLE) && (m->status != AST_DEVICE_INVALID)) {
04060                count++;
04061             }
04062             break;
04063          case QMC_FREE:
04064             /* Count free members in the queue */
04065             if (!m->paused && ((m->status == AST_DEVICE_UNKNOWN) || (m->status == AST_DEVICE_NOT_INUSE))) {
04066                count++;
04067             }
04068             break;
04069          default:
04070             count++;
04071             break;
04072          }
04073          ao2_ref(m, -1);
04074       }
04075       ast_mutex_unlock(&q->lock);
04076    } else
04077       ast_log(LOG_WARNING, "queue %s was not found\n", name);
04078 
04079    snprintf(buf, len, "%d", count);
04080    ast_module_user_remove(lu);
04081 
04082    return 0;
04083 }

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

Definition at line 4128 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), call_queue::lock, LOG_ERROR, LOG_WARNING, member::membername, call_queue::members, and call_queue::name.

04129 {
04130    struct ast_module_user *u;
04131    struct call_queue *q;
04132    struct member *m;
04133 
04134    /* Ensure an otherwise empty list doesn't return garbage */
04135    buf[0] = '\0';
04136 
04137    if (ast_strlen_zero(data)) {
04138       ast_log(LOG_ERROR, "QUEUE_MEMBER_LIST requires an argument: queuename\n");
04139       return -1;
04140    }
04141    
04142    u = ast_module_user_add(chan);
04143 
04144    AST_LIST_LOCK(&queues);
04145    AST_LIST_TRAVERSE(&queues, q, list) {
04146       if (!strcasecmp(q->name, data)) {
04147          ast_mutex_lock(&q->lock);
04148          break;
04149       }
04150    }
04151    AST_LIST_UNLOCK(&queues);
04152 
04153    if (q) {
04154       int buflen = 0, count = 0;
04155       struct ao2_iterator mem_iter = ao2_iterator_init(q->members, 0);
04156 
04157       while ((m = ao2_iterator_next(&mem_iter))) {
04158          /* strcat() is always faster than printf() */
04159          if (count++) {
04160             strncat(buf + buflen, ",", len - buflen - 1);
04161             buflen++;
04162          }
04163          strncat(buf + buflen, m->membername, len - buflen - 1);
04164          buflen += strlen(m->membername);
04165          /* Safeguard against overflow (negative length) */
04166          if (buflen >= len - 2) {
04167             ao2_ref(m, -1);
04168             ast_log(LOG_WARNING, "Truncating list\n");
04169             break;
04170          }
04171          ao2_ref(m, -1);
04172       }
04173       ast_mutex_unlock(&q->lock);
04174    } else
04175       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04176 
04177    /* We should already be terminated, but let's make sure. */
04178    buf[len - 1] = '\0';
04179    ast_module_user_remove(u);
04180 
04181    return 0;
04182 }

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

Definition at line 4085 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_load_realtime(), ast_log(), ast_module_user_add, ast_module_user_remove, ast_mutex_lock(), ast_mutex_unlock(), ast_strlen_zero(), ast_variables_destroy(), call_queue::count, call_queue::lock, LOG_ERROR, LOG_WARNING, call_queue::name, and var.

04086 {
04087    int count = 0;
04088    struct call_queue *q;
04089    struct ast_module_user *lu;
04090    struct ast_variable *var = NULL;
04091 
04092    buf[0] = '\0';
04093    
04094    if (ast_strlen_zero(data)) {
04095       ast_log(LOG_ERROR, "%s requires an argument: queuename\n", cmd);
04096       return -1;
04097    }
04098 
04099    lu = ast_module_user_add(chan);
04100    
04101    AST_LIST_LOCK(&queues);
04102    AST_LIST_TRAVERSE(&queues, q, list) {
04103       if (!strcasecmp(q->name, data)) {
04104          ast_mutex_lock(&q->lock);
04105          break;
04106       }
04107    }
04108    AST_LIST_UNLOCK(&queues);
04109 
04110    if (q) {
04111       count = q->count;
04112       ast_mutex_unlock(&q->lock);
04113    } else if ((var = ast_load_realtime("queues", "name", data, NULL))) {
04114       /* if the queue is realtime but was not found in memory, this
04115        * means that the queue had been deleted from memory since it was 
04116        * "dead." This means it has a 0 waiting count
04117        */
04118       count = 0;
04119       ast_variables_destroy(var);
04120    } else
04121       ast_log(LOG_WARNING, "queue %s was not found\n", data);
04122 
04123    snprintf(buf, len, "%d", count);
04124    ast_module_user_remove(lu);
04125    return 0;
04126 }

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

References call_queue::announce, call_queue::announcefrequency, call_queue::announceholdtime, ANNOUNCEHOLDTIME_ALWAYS, ANNOUNCEHOLDTIME_ONCE, 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, strsep(), call_queue::timeout, call_queue::timeoutrestart, call_queue::weight, and call_queue::wrapuptime.

Referenced by find_queue_by_name_rt(), and reload_queues().

00961 {
00962    if (!strcasecmp(param, "musicclass") || 
00963       !strcasecmp(param, "music") || !strcasecmp(param, "musiconhold")) {
00964       ast_copy_string(q->moh, val, sizeof(q->moh));
00965    } else if (!strcasecmp(param, "announce")) {
00966       ast_copy_string(q->announce, val, sizeof(q->announce));
00967    } else if (!strcasecmp(param, "context")) {
00968       ast_copy_string(q->context, val, sizeof(q->context));
00969    } else if (!strcasecmp(param, "timeout")) {
00970       q->timeout = atoi(val);
00971       if (q->timeout < 0)
00972          q->timeout = DEFAULT_TIMEOUT;
00973    } else if (!strcasecmp(param, "ringinuse")) {
00974       q->ringinuse = ast_true(val);
00975    } else if (!strcasecmp(param, "setinterfacevar")) {
00976       q->setinterfacevar = ast_true(val);
00977    } else if (!strcasecmp(param, "monitor-join")) {
00978       monjoin_dep_warning();
00979       q->monjoin = ast_true(val);
00980    } else if (!strcasecmp(param, "monitor-format")) {
00981       ast_copy_string(q->monfmt, val, sizeof(q->monfmt));
00982    } else if (!strcasecmp(param, "queue-youarenext")) {
00983       ast_copy_string(q->sound_next, val, sizeof(q->sound_next));
00984    } else if (!strcasecmp(param, "queue-thereare")) {
00985       ast_copy_string(q->sound_thereare, val, sizeof(q->sound_thereare));
00986    } else if (!strcasecmp(param, "queue-callswaiting")) {
00987       ast_copy_string(q->sound_calls, val, sizeof(q->sound_calls));
00988    } else if (!strcasecmp(param, "queue-holdtime")) {
00989       ast_copy_string(q->sound_holdtime, val, sizeof(q->sound_holdtime));
00990    } else if (!strcasecmp(param, "queue-minutes")) {
00991       ast_copy_string(q->sound_minutes, val, sizeof(q->sound_minutes));
00992    } else if (!strcasecmp(param, "queue-seconds")) {
00993       ast_copy_string(q->sound_seconds, val, sizeof(q->sound_seconds));
00994    } else if (!strcasecmp(param, "queue-lessthan")) {
00995       ast_copy_string(q->sound_lessthan, val, sizeof(q->sound_lessthan));
00996    } else if (!strcasecmp(param, "queue-thankyou")) {
00997       ast_copy_string(q->sound_thanks, val, sizeof(q->sound_thanks));
00998    } else if (!strcasecmp(param, "queue-reporthold")) {
00999       ast_copy_string(q->sound_reporthold, val, sizeof(q->sound_reporthold));
01000    } else if (!strcasecmp(param, "announce-frequency")) {
01001       q->announcefrequency = atoi(val);
01002    } else if (!strcasecmp(param, "announce-round-seconds")) {
01003       q->roundingseconds = atoi(val);
01004       if (q->roundingseconds>60 || q->roundingseconds<0) {
01005          if (linenum >= 0) {
01006             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01007                "using 0 instead for queue '%s' at line %d of queues.conf\n",
01008                val, param, q->name, linenum);
01009          } else {
01010             ast_log(LOG_WARNING, "'%s' isn't a valid value for %s "
01011                "using 0 instead for queue '%s'\n", val, param, q->name);
01012          }
01013          q->roundingseconds=0;
01014       }
01015    } else if (!strcasecmp(param, "announce-holdtime")) {
01016       if (!strcasecmp(val, "once"))
01017          q->announceholdtime = ANNOUNCEHOLDTIME_ONCE;
01018       else if (ast_true(val))
01019          q->announceholdtime = ANNOUNCEHOLDTIME_ALWAYS;
01020       else
01021          q->announceholdtime = 0;
01022    } else if (!strcasecmp(param, "periodic-announce")) {
01023       if (strchr(val, '|')) {
01024          char *s, *buf = ast_strdupa(val);
01025          unsigned int i = 0;
01026 
01027          while ((s = strsep(&buf, "|"))) {
01028             ast_copy_string(q->sound_periodicannounce[i], s, sizeof(q->sound_periodicannounce[i]));
01029             i++;
01030             if (i == MAX_PERIODIC_ANNOUNCEMENTS)
01031                break;
01032          }
01033       } else {
01034          ast_copy_string(q->sound_periodicannounce[0], val, sizeof(q->sound_periodicannounce[0]));
01035       }
01036    } else if (!strcasecmp(param, "periodic-announce-frequency")) {
01037       q->periodicannouncefrequency = atoi(val);
01038    } else if (!strcasecmp(param, "retry")) {
01039       q->retry = atoi(val);
01040       if (q->retry <= 0)
01041          q->retry = DEFAULT_RETRY;
01042    } else if (!strcasecmp(param, "wrapuptime")) {
01043       q->wrapuptime = atoi(val);
01044    } else if (!strcasecmp(param, "autofill")) {
01045       q->autofill = ast_true(val);
01046    } else if (!strcasecmp(param, "monitor-type")) {
01047       if (!strcasecmp(val, "mixmonitor"))
01048          q->montype = 1;
01049    } else if (!strcasecmp(param, "autopause")) {
01050       q->autopause = ast_true(val);
01051    } else if (!strcasecmp(param, "maxlen")) {
01052       q->maxlen = atoi(val);
01053       if (q->maxlen < 0)
01054          q->maxlen = 0;
01055    } else if (!strcasecmp(param, "ringlimit")) {
01056       q->ringlimit = atoi(val);
01057       if (q->ringlimit < 0)
01058          q->ringlimit = 0;
01059    } else if (!strcasecmp(param, "servicelevel")) {
01060       q->servicelevel= atoi(val);
01061    } else if (!strcasecmp(param, "strategy")) {
01062       q->strategy = strat2int(val);
01063       if (q->strategy < 0) {
01064          ast_log(LOG_WARNING, "'%s' isn't a valid strategy for queue '%s', using ringall instead\n",
01065             val, q->name);
01066          q->strategy = QUEUE_STRATEGY_RINGALL;
01067       }
01068    } else if (!strcasecmp(param, "joinempty")) {
01069       if (!strcasecmp(val, "strict"))
01070          q->joinempty = QUEUE_EMPTY_STRICT;
01071       else if (ast_true(val))
01072          q->joinempty = QUEUE_EMPTY_NORMAL;
01073       else
01074          q->joinempty = 0;
01075    } else if (!strcasecmp(param, "leavewhenempty")) {
01076       if (!strcasecmp(val, "strict"))
01077          q->leavewhenempty = QUEUE_EMPTY_STRICT;
01078       else if (ast_true(val))
01079          q->leavewhenempty = QUEUE_EMPTY_NORMAL;
01080       else
01081          q->leavewhenempty = 0;
01082    } else if (!strcasecmp(param, "eventmemberstatus")) {
01083       q->maskmemberstatus = !ast_true(val);
01084    } else if (!strcasecmp(param, "eventwhencalled")) {
01085       if (!strcasecmp(val, "vars")) {
01086          q->eventwhencalled = QUEUE_EVENT_VARIABLES;
01087       } else {
01088          q->eventwhencalled = ast_true(val) ? 1 : 0;
01089       }
01090    } else if (!strcasecmp(param, "reportholdtime")) {
01091       q->reportholdtime = ast_true(val);
01092    } else if (!strcasecmp(param, "memberdelay")) {
01093       q->memberdelay = atoi(val);
01094    } else if (!strcasecmp(param, "weight")) {
01095       q->weight = atoi(val);
01096       if (q->weight)
01097          use_weight++;
01098       /* With Realtime queues, if the last queue using weights is deleted in realtime,
01099          we will not see any effect on use_weight until next reload. */
01100    } else if (!strcasecmp(param, "timeoutrestart")) {
01101       q->timeoutrestart = ast_true(val);
01102    } else if (failunknown) {
01103       if (linenum >= 0) {
01104          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s at line %d of queues.conf\n",
01105             q->name, param, linenum);
01106       } else {
01107          ast_log(LOG_WARNING, "Unknown keyword in queue '%s': %s\n", q->name, param);
01108       }
01109    }
01110 }

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

Definition at line 4547 of file app_queue.c.

References __queues_show().

Referenced by __queues_show().

04548 {
04549    return __queues_show(NULL, 0, fd, argc, argv);
04550 }

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

Definition at line 1639 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), call_queue::holdtime, call_queue::lock, and queue_ent::parent.

Referenced by try_calling().

01640 {
01641    int oldvalue;
01642 
01643    /* Calculate holdtime using a recursive boxcar filter */
01644    /* Thanks to SRT for this contribution */
01645    /* 2^2 (4) is the filter coefficient; a higher exponent would give old entries more weight */
01646 
01647    ast_mutex_lock(&qe->parent->lock);
01648    oldvalue = qe->parent->holdtime;
01649    qe->parent->holdtime = (((oldvalue << 2) - oldvalue) + newholdtime) >> 2;
01650    ast_mutex_unlock(&qe->parent->lock);
01651 }

static void record_abandoned ( struct queue_ent qe  )  [static]

Definition at line 2057 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), call_queue::callsabandoned, queue_ent::chan, EVENT_FLAG_AGENT, call_queue::lock, manager_event(), call_queue::name, queue_ent::opos, queue_ent::parent, queue_ent::pos, and queue_ent::start.

Referenced by queue_exec(), and try_calling().

02058 {
02059    ast_mutex_lock(&qe->parent->lock);
02060    manager_event(EVENT_FLAG_AGENT, "QueueCallerAbandon",
02061       "Queue: %s\r\n"
02062       "Uniqueid: %s\r\n"
02063       "Position: %d\r\n"
02064       "OriginalPosition: %d\r\n"
02065       "HoldTime: %d\r\n",
02066       qe->parent->name, qe->chan->uniqueid, qe->pos, qe->opos, (int)(time(NULL) - qe->start));
02067 
02068    qe->parent->callsabandoned++;
02069    ast_mutex_unlock(&qe->parent->lock);
02070 }

static int reload ( void   )  [static]

Definition at line 5068 of file app_queue.c.

References reload_queues().

05069 {
05070    reload_queues();
05071    return 0;
05072 }

static void reload_queue_members ( void   )  [static]

Definition at line 3343 of file app_queue.c.

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

Referenced by load_module().

03344 {
03345    char *cur_ptr; 
03346    char *queue_name;
03347    char *member;
03348    char *interface;
03349    char *membername = NULL;
03350    char *penalty_tok;
03351    int penalty = 0;
03352    char *paused_tok;
03353    int paused = 0;
03354    struct ast_db_entry *db_tree;
03355    struct ast_db_entry *entry;
03356    struct call_queue *cur_queue;
03357    char queue_data[PM_MAX_LEN];
03358 
03359    AST_LIST_LOCK(&queues);
03360 
03361    /* Each key in 'pm_family' is the name of a queue */
03362    db_tree = ast_db_gettree(pm_family, NULL);
03363    for (entry = db_tree; entry; entry = entry->next) {
03364 
03365       queue_name = entry->key + strlen(pm_family) + 2;
03366 
03367       AST_LIST_TRAVERSE(&queues, cur_queue, list) {
03368          ast_mutex_lock(&cur_queue->lock);
03369          if (!strcmp(queue_name, cur_queue->name))
03370             break;
03371          ast_mutex_unlock(&cur_queue->lock);
03372       }
03373       
03374       if (!cur_queue)
03375          cur_queue = load_realtime_queue(queue_name);
03376 
03377       if (!cur_queue) {
03378          /* If the queue no longer exists, remove it from the
03379           * database */
03380          ast_log(LOG_WARNING, "Error loading persistent queue: '%s': it does not exist\n", queue_name);
03381          ast_db_del(pm_family, queue_name);
03382          continue;
03383       } else
03384          ast_mutex_unlock(&cur_queue->lock);
03385 
03386       if (ast_db_get(pm_family, queue_name, queue_data, PM_MAX_LEN))
03387          continue;
03388 
03389       cur_ptr = queue_data;
03390       while ((member = strsep(&cur_ptr, "|"))) {
03391          if (ast_strlen_zero(member))
03392             continue;
03393 
03394          interface = strsep(&member, ";");
03395          penalty_tok = strsep(&member, ";");
03396          paused_tok = strsep(&member, ";");
03397          membername = strsep(&member, ";");
03398 
03399          if (!penalty_tok) {
03400             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (penalty)\n", queue_name);
03401             break;
03402          }
03403          penalty = strtol(penalty_tok, NULL, 10);
03404          if (errno == ERANGE) {
03405             ast_log(LOG_WARNING, "Error converting penalty: %s: Out of range.\n", penalty_tok);
03406             break;
03407          }
03408          
03409          if (!paused_tok) {
03410             ast_log(LOG_WARNING, "Error parsing persistent member string for '%s' (paused)\n", queue_name);
03411             break;
03412          }
03413          paused = strtol(paused_tok, NULL, 10);
03414          if ((errno == ERANGE) || paused < 0 || paused > 1) {
03415             ast_log(LOG_WARNING, "Error converting paused: %s: Expected 0 or 1.\n", paused_tok);
03416             break;
03417          }
03418          if (ast_strlen_zero(membername))
03419             membername = interface;
03420 
03421          if (option_debug)
03422             ast_log(LOG_DEBUG, "Reload Members: Queue: %s  Member: %s  Name: %s  Penalty: %d  Paused: %d\n", queue_name, interface, membername, penalty, paused);
03423          
03424          if (add_to_queue(queue_name, interface, membername, penalty, paused, 0) == RES_OUTOFMEMORY) {
03425             ast_log(LOG_ERROR, "Out of Memory when reloading persistent queue member\n");
03426             break;
03427          }
03428       }
03429    }
03430 
03431    AST_LIST_UNLOCK(&queues);
03432    if (db_tree) {
03433       ast_log(LOG_NOTICE, "Queue members successfully reloaded from database.\n");
03434       ast_db_freetree(db_tree);
03435    }
03436 }

static int reload_queues ( void   )  [static]

Definition at line 4227 of file app_queue.c.

References add_to_interfaces(), alloc_queue(), ao2_find(), ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), ao2_unlink(), AST_APP_ARG, ast_category_browse(), ast_config_load(), AST_DECLARE_APP_ARGS, AST_LIST_INSERT_HEAD, AST_LIST_LOCK, AST_LIST_TRAVERSE, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), AST_NONSTANDARD_APP_ARGS, ast_strlen_zero(), ast_true(), ast_variable_browse(), ast_variable_retrieve(), clear_queue(), create_queue_member(), call_queue::dead, member::delme, member::dynamic, call_queue::found, init_queue(), member::interface, call_queue::lock, LOG_NOTICE, LOG_WARNING, call_queue::membercount, call_queue::members, call_queue::name, parse(), member::paused, queue_set_param(), QUEUE_STRATEGY_ROUNDROBIN, call_queue::realtime, remove_from_interfaces(), rr_dep_warning(), call_queue::strategy, and var.

Referenced by load_module(), and reload().

04228 {
04229    struct call_queue *q;
04230    struct ast_config *cfg;
04231    char *cat, *tmp;
04232    struct ast_variable *var;
04233    struct member *cur, *newm;
04234    struct ao2_iterator mem_iter;
04235    int new;
04236    const char *general_val = NULL;
04237    char parse[80];
04238    char *interface;
04239    char *membername = NULL;
04240    int penalty;
04241    AST_DECLARE_APP_ARGS(args,
04242       AST_APP_ARG(interface);
04243       AST_APP_ARG(penalty);
04244       AST_APP_ARG(membername);
04245    );
04246    
04247    if (!(cfg = ast_config_load("queues.conf"))) {
04248       ast_log(LOG_NOTICE, "No call queueing config file (queues.conf), so no call queues\n");
04249       return 0;
04250    }
04251    AST_LIST_LOCK(&queues);
04252    use_weight=0;
04253    /* Mark all non-realtime queues as dead for the moment */
04254    AST_LIST_TRAVERSE(&queues, q, list) {
04255       if (!q->realtime) {
04256          q->dead = 1;
04257          q->found = 0;
04258       }
04259    }
04260 
04261    /* Chug through config file */
04262    cat = NULL;
04263    while ((cat = ast_category_browse(cfg, cat)) ) {
04264       if (!strcasecmp(cat, "general")) {  
04265          /* Initialize global settings */
04266          queue_debug = 0;
04267          if ((general_val = ast_variable_retrieve(cfg, "general", "debug")))
04268             queue_debug = ast_true(general_val);
04269          queue_persistent_members = 0;
04270          if ((general_val = ast_variable_retrieve(cfg, "general", "persistentmembers")))
04271             queue_persistent_members = ast_true(general_val);
04272          autofill_default = 0;
04273          if ((general_val = ast_variable_retrieve(cfg, "general", "autofill")))
04274             autofill_default = ast_true(general_val);
04275          montype_default = 0;
04276          if ((general_val = ast_variable_retrieve(cfg, "general", "monitor-type")))
04277             if (!strcasecmp(general_val, "mixmonitor"))
04278                montype_default = 1;
04279       } else { /* Define queue */
04280          /* Look for an existing one */
04281          AST_LIST_TRAVERSE(&queues, q, list) {
04282             if (!strcmp(q->name, cat))
04283                break;
04284          }
04285          if (!q) {
04286             /* Make one then */
04287             if (!(q = alloc_queue(cat))) {
04288                /* TODO: Handle memory allocation failure */
04289             }
04290             new = 1;
04291          } else
04292             new = 0;
04293          if (q) {
04294             if (!new)
04295                ast_mutex_lock(&q->lock);
04296             /* Check if a queue with this name already exists */
04297             if (q->found) {
04298                ast_log(LOG_WARNING, "Queue '%s' already defined! Skipping!\n", cat);
04299                if (!new)
04300                   ast_mutex_unlock(&q->lock);
04301                continue;
04302             }
04303             /* Re-initialize the queue, and clear statistics */
04304             init_queue(q);
04305             clear_queue(q);
04306             mem_iter = ao2_iterator_init(q->members, 0);
04307             while ((cur = ao2_iterator_next(&mem_iter))) {
04308                if (!cur->dynamic) {
04309                   cur->delme = 1;
04310                }
04311                ao2_ref(cur, -1);
04312             }
04313             for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
04314                if (!strcasecmp(var->name, "member")) {
04315                   struct member tmpmem;
04316                   membername = NULL;
04317 
04318                   /* Add a new member */
04319                   ast_copy_string(parse, var->value, sizeof(parse));
04320                   
04321                   AST_NONSTANDARD_APP_ARGS(args, parse, ',');
04322 
04323                   interface = args.interface;
04324                   if (!ast_strlen_zero(args.penalty)) {
04325                      tmp = args.penalty;
04326                      while (*tmp && *tmp < 33) tmp++;
04327                      penalty = atoi(tmp);
04328                      if (penalty < 0) {
04329                         penalty = 0;
04330                      }
04331                   } else
04332                      penalty = 0;
04333 
04334                   if (!ast_strlen_zero(args.membername)) {
04335                      membername = args.membername;
04336                      while (*membername && *membername < 33) membername++;
04337                   }
04338 
04339                   /* Find the old position in the list */
04340                   ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
04341                   cur = ao2_find(q->members, &tmpmem, OBJ_POINTER | OBJ_UNLINK);
04342 
04343                   newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0);
04344                   ao2_link(q->members, newm);
04345                   ao2_ref(newm, -1);
04346                   newm = NULL;
04347 
04348                   if (cur)
04349                      ao2_ref(cur, -1);
04350                   else {
04351                      /* Add them to the master int list if necessary */
04352                      add_to_interfaces(interface);
04353                      q->membercount++;
04354                   }
04355                } else {
04356                   queue_set_param(q, var->name, var->value, var->lineno, 1);
04357                }
04358             }
04359 
04360             /* Free remaining members marked as delme */
04361             mem_iter = ao2_iterator_init(q->members, 0);
04362             while ((cur = ao2_iterator_next(&mem_iter))) {
04363                if (! cur->delme) {
04364                   ao2_ref(cur, -1);
04365                   continue;
04366                }
04367 
04368                q->membercount--;
04369                ao2_unlink(q->members, cur);
04370                remove_from_interfaces(cur->interface);
04371                ao2_ref(cur, -1);
04372             }
04373 
04374             if (q->strategy == QUEUE_STRATEGY_ROUNDROBIN)
04375                rr_dep_warning();
04376 
04377             if (new) {
04378                AST_LIST_INSERT_HEAD(&queues, q, list);
04379             } else
04380                ast_mutex_unlock(&q->lock);
04381          }
04382       }
04383    }
04384    ast_config_destroy(cfg);
04385    AST_LIST_TRAVERSE_SAFE_BEGIN(&queues, q, list) {
04386       if (q->dead) {
04387          AST_LIST_REMOVE_CURRENT(&queues, list);
04388          if (!q->count)
04389             destroy_queue(q);
04390          else
04391             ast_log(LOG_DEBUG, "XXX Leaking a little memory :( XXX\n");
04392       } else {
04393          ast_mutex_lock(&q->lock);
04394          mem_iter = ao2_iterator_init(q->members, 0);
04395          while ((cur = ao2_iterator_next(&mem_iter))) {
04396             if (cur->dynamic)
04397                q->membercount++;
04398             cur->status = ast_device_state(cur->interface);
04399             ao2_ref(cur, -1);
04400          }
04401          ast_mutex_unlock(&q->lock);
04402       }
04403    }
04404    AST_LIST_TRAVERSE_SAFE_END;
04405    AST_LIST_UNLOCK(&queues);
04406    return 1;
04407 }

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

Definition at line 920 of file app_queue.c.

References AST_LIST_LOCK, AST_LIST_REMOVE_CURRENT, AST_LIST_TRAVERSE_SAFE_BEGIN, AST_LIST_TRAVERSE_SAFE_END, AST_LIST_UNLOCK, ast_log(), free, member_interface::interface, interface_exists_global(), LOG_DEBUG, and option_debug.

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

00921 {
00922    struct member_interface *curint;
00923 
00924    if (interface_exists_global(interface))
00925       return 0;
00926 
00927    AST_LIST_LOCK(&interfaces);
00928    AST_LIST_TRAVERSE_SAFE_BEGIN(&interfaces, curint, list) {
00929       if (!strcasecmp(curint->interface, interface)) {
00930          if (option_debug)
00931             ast_log(LOG_DEBUG, "Removing %s from the list of interfaces that make up all of our queue members.\n", interface);
00932          AST_LIST_REMOVE_CURRENT(&interfaces, list);
00933          free(curint);
00934          break;
00935       }
00936    }
00937    AST_LIST_TRAVERSE_SAFE_END;
00938    AST_LIST_UNLOCK(&interfaces);
00939 
00940    return 0;
00941 }

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

Definition at line 3189 of file app_queue.c.

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

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

03190 {
03191    struct call_queue *q;
03192    struct member *mem, tmpmem;
03193    int res = RES_NOSUCHQUEUE;
03194 
03195    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
03196 
03197    AST_LIST_LOCK(&queues);
03198    AST_LIST_TRAVERSE(&queues, q, list) {
03199       ast_mutex_lock(&q->lock);
03200       if (strcmp(q->name, queuename)) {
03201          ast_mutex_unlock(&q->lock);
03202          continue;
03203       }
03204 
03205       if ((mem = ao2_find(q->members, &tmpmem, OBJ_POINTER))) {
03206          /* XXX future changes should beware of this assumption!! */
03207          if (!mem->dynamic) {
03208             res = RES_NOT_DYNAMIC;
03209             ao2_ref(mem, -1);
03210             ast_mutex_unlock(&q->lock);
03211             break;
03212          }
03213          q->membercount--;
03214          manager_event(EVENT_FLAG_AGENT, "QueueMemberRemoved",
03215             "Queue: %s\r\n"
03216             "Location: %s\r\n"
03217             "MemberName: %s\r\n",
03218             q->name, mem->interface, mem->membername);
03219          ao2_unlink(q->members, mem);
03220          ao2_ref(mem, -1);
03221 
03222          if (queue_persistent_members)
03223             dump_queue_members(q);
03224          
03225          res = RES_OKAY;
03226       } else {
03227          res = RES_EXISTS;
03228       }
03229       ast_mutex_unlock(&q->lock);
03230       break;
03231    }
03232 
03233    if (res == RES_OKAY)
03234       remove_from_interfaces(interface);
03235 
03236    AST_LIST_UNLOCK(&queues);
03237 
03238    return res;
03239 }

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

References ast_channel::adsicpe, ast_channel::appl, ast_call(), ast_cdr_busy(), ast_channel_inherit_variables(), ast_channel_lock, ast_channel_unlock, AST_DEVICE_NOT_INUSE, ast_device_state(), AST_DEVICE_UNKNOWN, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_request(), ast_strdup, ast_strlen_zero(), ast_verbose(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::cid, ast_callerid::cid_ani, ast_callerid::cid_name, ast_callerid::cid_num, compare_weight(), ast_channel::context, ast_channel::data, ast_channel::dialcontext, do_hang(), EVENT_FLAG_AGENT, call_queue::eventwhencalled, ast_channel::exten, free, member::interface, callattempt::interface, callattempt::lastcall, call_queue::lock, LOG_DEBUG, LOG_NOTICE, manager_event(), callattempt::member, member::membername, call_queue::name, ast_channel::nativeformats, option_debug, option_verbose, queue_ent::parent, member::paused, pbx_builtin_getvar_helper(), ast_channel::priority, QUEUE_EVENT_VARIABLES, member::ringcount, call_queue::ringinuse, call_queue::rrpos, member::status, callattempt::stillgoing, update_status(), vars2manager(), VERBOSE_PREFIX_3, ast_channel::whentohangup, and call_queue::wrapuptime.

Referenced by ring_one().

01795 {
01796    int res;
01797    int status;
01798    char tech[256];
01799    char *location;
01800    const char *macrocontext, *macroexten;
01801 
01802    /* on entry here, we know that tmp->chan == NULL */
01803    if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
01804       if (queue_debug)
01805          ast_log(LOG_NOTICE, "Wrapuptime not yet expired for %s\n", tmp->interface);
01806       if (qe->chan->cdr)
01807          ast_cdr_busy(qe->chan->cdr);
01808       tmp->stillgoing = 0;
01809       (*busies)++;
01810       return 0;
01811    }
01812 
01813    if (!qe->parent->ringinuse && (tmp->member->status != AST_DEVICE_NOT_INUSE) && (tmp->member->status != AST_DEVICE_UNKNOWN)) {
01814       if (queue_debug)
01815          ast_log(LOG_NOTICE, "%s in use, can't receive call\n", tmp->interface);
01816       if (qe->chan->cdr)
01817          ast_cdr_busy(qe->chan->cdr);
01818       tmp->stillgoing = 0;
01819       return 0;
01820    }
01821 
01822    if (tmp->member->paused) {
01823       if (queue_debug)
01824          ast_log(LOG_NOTICE, "%s paused, can't receive call\n", tmp->interface);
01825       if (qe->chan->cdr)
01826          ast_cdr_busy(qe->chan->cdr);
01827       tmp->stillgoing = 0;
01828       return 0;
01829    }
01830    if (use_weight && compare_weight(qe->parent,tmp->member)) {
01831       if (queue_debug)
01832          ast_log(LOG_NOTICE, "Priority queue delaying call to %s:%s\n", qe->parent->name, tmp->interface);
01833       if (qe->chan->cdr)
01834          ast_cdr_busy(qe->chan->cdr);
01835       tmp->stillgoing = 0;
01836       (*busies)++;
01837       return 0;
01838    }
01839 
01840    ast_copy_string(tech, tmp->interface, sizeof(tech));
01841    if ((location = strchr(tech, '/')))
01842       *location++ = '\0';
01843    else
01844       location = "";
01845 
01846    /* Request the peer */
01847    tmp->chan = ast_request(tech, qe->chan->nativeformats, location, &status);
01848    if (!tmp->chan) {       /* If we can't, just go on to the next call */
01849       if (queue_debug)
01850          ast_log(LOG_NOTICE, "Unable to create channel of type '%s' for Queue\n", tech);
01851       if (qe->chan->cdr)
01852          ast_cdr_busy(qe->chan->cdr);
01853       tmp->stillgoing = 0;
01854 
01855       update_status(tmp->member->interface, ast_device_state(tmp->member->interface));
01856 
01857       ast_mutex_lock(&qe->parent->lock);
01858       qe->parent->rrpos++;
01859       ast_mutex_unlock(&qe->parent->lock);
01860 
01861       (*busies)++;
01862       return 0;
01863    }
01864    
01865    /* Increment ring count */
01866    tmp->member->ringcount++;
01867    tmp->chan->appl = "AppQueue";
01868    tmp->chan->data = "(Outgoing Line)";
01869    tmp->chan->whentohangup = 0;
01870    if (tmp->chan->cid.cid_num)
01871       free(tmp->chan->cid.cid_num);
01872    tmp->chan->cid.cid_num = ast_strdup(qe->chan->cid.cid_num);
01873    if (tmp->chan->cid.cid_name)
01874       free(tmp->chan->cid.cid_name);
01875    tmp->chan->cid.cid_name = ast_strdup(qe->chan->cid.cid_name);
01876    if (tmp->chan->cid.cid_ani)
01877       free(tmp->chan->cid.cid_ani);
01878    tmp->chan->cid.cid_ani = ast_strdup(qe->chan->cid.cid_ani);
01879 
01880    /* Inherit specially named variables from parent channel */
01881    ast_channel_inherit_variables(qe->chan, tmp->chan);
01882 
01883    /* Presense of ADSI CPE on outgoing channel follows ours */
01884    tmp->chan->adsicpe = qe->chan->adsicpe;
01885 
01886    /* Inherit context and extension */
01887    ast_channel_lock(qe->chan);
01888    macrocontext = pbx_builtin_getvar_helper(qe->chan, "MACRO_CONTEXT");
01889    if (!ast_strlen_zero(macrocontext))
01890       ast_copy_string(tmp->chan->dialcontext, macrocontext, sizeof(tmp->chan->dialcontext));
01891    else
01892       ast_copy_string(tmp->chan->dialcontext, qe->chan->context, sizeof(tmp->chan->dialcontext));
01893    macroexten = pbx_builtin_getvar_helper(qe->chan, "MACRO_EXTEN");
01894    if (!ast_strlen_zero(macroexten))
01895       ast_copy_string(tmp->chan->exten, macroexten, sizeof(tmp->chan->exten));
01896    else
01897       ast_copy_string(tmp->chan->exten, qe->chan->exten, sizeof(tmp->chan->exten));
01898    ast_channel_unlock(qe->chan);
01899 
01900    /* Place the call, but don't wait on the answer */
01901    if ((res = ast_call(tmp->chan, location, 0))) {
01902       /* Again, keep going even if there's an error */
01903       if (option_debug)
01904          ast_log(LOG_DEBUG, "ast call on peer returned %d\n", res);
01905       if (option_verbose > 2)
01906          ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", tmp->interface);
01907       do_hang(tmp);
01908       (*busies)++;
01909       return 0;
01910    } else if (qe->parent->eventwhencalled) {
01911       char vars[2048];
01912 
01913       manager_event(EVENT_FLAG_AGENT, "AgentCalled",
01914                "AgentCalled: %s\r\n"
01915                "AgentName: %s\r\n"
01916                "ChannelCalling: %s\r\n"
01917                "CallerID: %s\r\n"
01918                "CallerIDName: %s\r\n"
01919                "Context: %s\r\n"
01920                "Extension: %s\r\n"
01921                "Priority: %d\r\n"
01922                "%s",
01923                tmp->interface, tmp->member->membername, qe->chan->name,
01924                tmp->chan->cid.cid_num ? tmp->chan->cid.cid_num : "unknown",
01925                tmp->chan->cid.cid_name ? tmp->chan->cid.cid_name : "unknown",
01926                qe->chan->context, qe->chan->exten, qe->chan->priority,
01927                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
01928       if (option_verbose > 2)
01929          ast_verbose(VERBOSE_PREFIX_3 "Called %s\n", tmp->interface);
01930    }
01931 
01932    return 1;
01933 }

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

01960 {
01961    int ret = 0;
01962 
01963    while (ret == 0) {
01964       struct callattempt *best = find_best(outgoing);
01965       if (!best) {
01966          if (option_debug)
01967             ast_log(LOG_DEBUG, "Nobody left to try ringing in queue\n");
01968          break;
01969       }
01970       if (qe->parent->strategy == QUEUE_STRATEGY_RINGALL) {
01971          struct callattempt *cur;
01972          /* Ring everyone who shares this best metric (for ringall) */
01973          for (cur = outgoing; cur; cur = cur->q_next) {
01974             if (cur->stillgoing && !cur->chan && cur->metric <= best->metric) {
01975                if (option_debug)
01976                   ast_log(LOG_DEBUG, "(Parallel) Trying '%s' with metric %d\n", cur->interface, cur->metric);
01977                ret |= ring_entry(qe, cur, busies);
01978             }
01979          }
01980       } else {
01981          /* Ring just the best channel */
01982          if (option_debug)
01983             ast_log(LOG_DEBUG, "Trying '%s' with metric %d\n", best->interface, best->metric);
01984          ret = ring_entry(qe, best, busies);
01985       }
01986    }
01987 
01988    return ret;
01989 }

static void rna ( int  rnatime,
struct queue_ent qe,
char *  interface,
char *  membername 
) [static]

RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer.

Definition at line 2073 of file app_queue.c.

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

02074 {
02075    if (option_verbose > 2)
02076       ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
02077    ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
02078    if (qe->parent->autopause) {
02079       if (!set_member_paused(qe->parent->name, interface, 1)) {
02080          if (option_verbose > 2)
02081             ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
02082       } else {
02083          if (option_verbose > 2)
02084             ast_verbose( VERBOSE_PREFIX_3 "Failed to pause Queue Member %s in queue %s!\n", interface, qe->parent->name);
02085       }
02086    }
02087    return;
02088 }

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

Definition at line 3554 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, parse(), pbx_builtin_setvar_helper(), remove_from_queue(), RES_EXISTS, RES_NOSUCHQUEUE, RES_NOT_DYNAMIC, and RES_OKAY.

Referenced by load_module().

03555 {
03556    int res=-1;
03557    struct ast_module_user *lu;
03558    char *parse, *temppos = NULL;
03559    int priority_jump = 0;
03560    AST_DECLARE_APP_ARGS(args,
03561       AST_APP_ARG(queuename);
03562       AST_APP_ARG(interface);
03563       AST_APP_ARG(options);
03564    );
03565 
03566 
03567    if (ast_strlen_zero(data)) {
03568       ast_log(LOG_WARNING, "RemoveQueueMember requires an argument (queuename[|interface[|options]])\n");
03569       return -1;
03570    }
03571 
03572    parse = ast_strdupa(data);
03573 
03574    AST_STANDARD_APP_ARGS(args, parse);
03575 
03576    lu = ast_module_user_add(chan);
03577 
03578    if (ast_strlen_zero(args.interface)) {
03579       args.interface = ast_strdupa(chan->name);
03580       temppos = strrchr(args.interface, '-');
03581       if (temppos)
03582          *temppos = '\0';
03583    }
03584 
03585    if (args.options) {
03586       if (strchr(args.options, 'j'))
03587          priority_jump = 1;
03588    }
03589 
03590    switch (remove_from_queue(args.queuename, args.interface)) {
03591    case RES_OKAY:
03592       ast_queue_log(args.queuename, chan->uniqueid, args.interface, "REMOVEMEMBER", "%s", "");
03593       ast_log(LOG_NOTICE, "Removed interface '%s' from queue '%s'\n", args.interface, args.queuename);
03594       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "REMOVED");
03595       res = 0;
03596       break;
03597    case RES_EXISTS:
03598       ast_log(LOG_DEBUG, "Unable to remove interface '%s' from queue '%s': Not there\n", args.interface, args.queuename);
03599       if (priority_jump || ast_opt_priority_jumping)
03600          ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
03601       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTINQUEUE");
03602       res = 0;
03603       break;
03604    case RES_NOSUCHQUEUE:
03605       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': No such queue\n", args.queuename);
03606       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOSUCHQUEUE");
03607       res = 0;
03608       break;
03609    case RES_NOT_DYNAMIC:
03610       ast_log(LOG_WARNING, "Unable to remove interface from queue '%s': '%s' is not a dynamic member\n", args.queuename, args.interface);
03611       pbx_builtin_setvar_helper(chan, "RQMSTATUS", "NOTDYNAMIC");
03612       res = 0;
03613       break;
03614    }
03615 
03616    ast_module_user_remove(lu);
03617 
03618    return res;
03619 }

static void rr_dep_warning ( void   )  [static]

Definition at line 452 of file app_queue.c.

References ast_log(), and LOG_NOTICE.

Referenced by reload_queues().

00453 {
00454    static unsigned int warned = 0;
00455 
00456    if (!warned) {
00457       ast_log(LOG_NOTICE, "The 'roundrobin' queue strategy is deprecated. Please use the 'rrmemory' strategy instead.\n");
00458       warned = 1;
00459    }
00460 }

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

Definition at line 1112 of file app_queue.c.

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

Referenced by update_realtime_members().

01113 {
01114    struct member *m, tmpmem;
01115    int penalty = 0;
01116    int paused  = 0;
01117 
01118    if (penalty_str) {
01119       penalty = atoi(penalty_str);
01120       if (penalty < 0)
01121          penalty = 0;
01122    }
01123 
01124    if (paused_str) {
01125       paused = atoi(paused_str);
01126       if (paused < 0)
01127          paused = 0;
01128    }
01129 
01130    /* Find the member, or the place to put a new one. */
01131    ast_copy_string(tmpmem.interface, interface, sizeof(tmpmem.interface));
01132    m = ao2_find(q->members, &tmpmem, OBJ_POINTER);
01133 
01134    /* Create a new one if not found, else update penalty */
01135    if (!m) {
01136       if ((m = create_queue_member(interface, membername, penalty, paused))) {
01137          m->dead = 0;
01138          m->realtime = 1;
01139          add_to_interfaces(interface);
01140          ao2_link(q->members, m);
01141          ao2_ref(m, -1);
01142          m = NULL;
01143          q->membercount++;
01144       }
01145    } else {
01146       m->dead = 0;   /* Do not delete this one. */
01147       if (paused_str)
01148          m->paused = paused;
01149       m->penalty = penalty;
01150       ao2_ref(m, -1);
01151    }
01152 }

static int say_periodic_announcement ( struct queue_ent qe  )  [static]

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

02016 {
02017    int res = 0;
02018    time_t now;
02019 
02020    /* Get the current time */
02021    time(&now);
02022 
02023    /* Check to see if it is time to announce */
02024    if ((now - qe->last_periodic_announce_time) < qe->parent->periodicannouncefrequency)
02025       return 0;
02026 
02027    /* Stop the music on hold so we can play our own file */
02028    ast_moh_stop(qe->chan);
02029 
02030    if (option_verbose > 2)
02031       ast_verbose(VERBOSE_PREFIX_3 "Playing periodic announcement\n");
02032 
02033    /* Check to make sure we have a sound file. If not, reset to the first sound file */
02034    if (qe->last_periodic_announce_sound >= MAX_PERIODIC_ANNOUNCEMENTS || !strlen(qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound])) {
02035       qe->last_periodic_announce_sound = 0;
02036    }
02037    
02038    /* play the announcement */
02039    res = play_file(qe->chan, qe->parent->sound_periodicannounce[qe->last_periodic_announce_sound]);
02040 
02041    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
02042       res = 0;
02043 
02044    /* Resume Music on Hold if the caller is going to stay in the queue */
02045    if (!res)
02046       ast_moh_start(qe->chan, qe->moh, NULL);
02047 
02048    /* update last_periodic_announce_time */
02049    qe->last_periodic_announce_time = now;
02050 
02051    /* Update the current periodic announcement to the next announcement */
02052    qe->last_periodic_announce_sound++;
02053    
02054    return res;
02055 }

static int say_position ( struct queue_ent qe  )  [static]

Definition at line 1532 of file app_queue.c.

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

01533 {
01534    int res = 0, avgholdmins, avgholdsecs;
01535    time_t now;
01536 
01537    /* Check to see if this is ludicrous -- if we just announced position, don't do it again*/
01538    time(&now);
01539    if ((now - qe->last_pos) < 15)
01540       return 0;
01541 
01542    /* If either our position has changed, or we are over the freq timer, say position */
01543    if ((qe->last_pos_said == qe->pos) && ((now - qe->last_pos) < qe->parent->announcefrequency))
01544       return 0;
01545 
01546    ast_moh_stop(qe->chan);
01547    /* Say we're next, if we are */
01548    if (qe->pos == 1) {
01549       res = play_file(qe->chan, qe->parent->sound_next);
01550       if (res)
01551          goto playout;
01552       else
01553          goto posout;
01554    } else {
01555       res = play_file(qe->chan, qe->parent->sound_thereare);
01556       if (res)
01557          goto playout;
01558       res = ast_say_number(qe->chan, qe->pos, AST_DIGIT_ANY, qe->chan->language, (char *) NULL); /* Needs gender */
01559       if (res)
01560          goto playout;
01561       res = play_file(qe->chan, qe->parent->sound_calls);
01562       if (res)
01563          goto playout;
01564    }
01565    /* Round hold time to nearest minute */
01566    avgholdmins = abs(((qe->parent->holdtime + 30) - (now - qe->start)) / 60);
01567 
01568    /* If they have specified a rounding then round the seconds as well */
01569    if (qe->parent->roundingseconds) {
01570       avgholdsecs = (abs(((qe->parent->holdtime + 30) - (now - qe->start))) - 60 * avgholdmins) / qe->parent->roundingseconds;
01571       avgholdsecs *= qe->parent->roundingseconds;
01572    } else {
01573       avgholdsecs = 0;
01574    }
01575 
01576    if (option_verbose > 2)
01577       ast_verbose(VERBOSE_PREFIX_3 "Hold time for %s is %d minutes %d seconds\n", qe->parent->name, avgholdmins, avgholdsecs);
01578 
01579    /* If the hold time is >1 min, if it's enabled, and if it's not
01580       supposed to be only once and we have already said it, say it */
01581    if ((avgholdmins+avgholdsecs) > 0 && (qe->parent->announceholdtime) &&
01582       (!(qe->parent->announceholdtime == ANNOUNCEHOLDTIME_ONCE) && qe->last_pos)) {
01583       res = play_file(qe->chan, qe->parent->sound_holdtime);
01584       if (res)
01585          goto playout;
01586 
01587       if (avgholdmins > 0) {
01588          if (avgholdmins < 2) {
01589             res = play_file(qe->chan, qe->parent->sound_lessthan);
01590             if (res)
01591                goto playout;
01592 
01593             res = ast_say_number(qe->chan, 2, AST_DIGIT_ANY, qe->chan->language, NULL);
01594             if (res)
01595                goto playout;
01596          } else {
01597             res = ast_say_number(qe->chan, avgholdmins, AST_DIGIT_ANY, qe->chan->language, NULL);
01598             if (res)
01599                goto playout;
01600          }
01601          
01602          res = play_file(qe->chan, qe->parent->sound_minutes);
01603          if (res)
01604             goto playout;
01605       }
01606       if (avgholdsecs>0) {
01607          res = ast_say_number(qe->chan, avgholdsecs, AST_DIGIT_ANY, qe->chan->language, NULL);
01608          if (res)
01609             goto playout;
01610 
01611          res = play_file(qe->chan, qe->parent->sound_seconds);
01612          if (res)
01613             goto playout;
01614       }
01615 
01616    }
01617 
01618 posout:
01619    if (option_verbose > 2)
01620       ast_verbose(VERBOSE_PREFIX_3 "Told %s in %s their queue position (which was %d)\n",
01621          qe->chan->name, qe->parent->name, qe->pos);
01622    res = play_file(qe->chan, qe->parent->sound_thanks);
01623 
01624 playout:
01625    if ((res > 0 && !valid_exit(qe, res)) || res < 0)
01626       res = 0;
01627 
01628    /* Set our last_pos indicators */
01629    qe->last_pos = now;
01630    qe->last_pos_said = qe->pos;
01631 
01632    /* Don't restart music on hold if we're about to exit the caller from the queue */
01633    if (!res)
01634       ast_moh_start(qe->chan, qe->moh, NULL);
01635 
01636    return res;
01637 }

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

Definition at line 3297 of file app_queue.c.

References ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_queue_log(), ast_strlen_zero(), dump_queue_members(), EVENT_FLAG_AGENT, member::interface, interface_exists(), call_queue::lock, 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().

03298 {
03299    int found = 0;
03300    struct call_queue *q;
03301    struct member *mem;
03302 
03303    /* Special event for when all queues are paused - individual events still generated */
03304    /* XXX In all other cases, we use the membername, but since this affects all queues, we cannot */
03305    if (ast_strlen_zero(queuename))
03306       ast_queue_log("NONE", "NONE", interface, (paused ? "PAUSEALL" : "UNPAUSEALL"), "%s", "");
03307 
03308    AST_LIST_LOCK(&queues);
03309    AST_LIST_TRAVERSE(&queues, q, list) {
03310       ast_mutex_lock(&q->lock);
03311       if (ast_strlen_zero(queuename) || !strcasecmp(q->name, queuename)) {
03312          if ((mem = interface_exists(q, interface))) {
03313             found++;
03314             if (mem->paused == paused)
03315                ast_log(LOG_DEBUG, "%spausing already-%spaused queue member %s:%s\n", (paused ? "" : "un"), (paused ? "" : "un"), q->name, interface);
03316             mem->paused = paused;
03317 
03318             if (queue_persistent_members)
03319                dump_queue_members(q);
03320 
03321             if (mem->realtime)
03322                update_realtime_member_field(mem, q->name, "paused", paused ? "1" : "0");
03323 
03324             ast_queue_log(q->name, "NONE", mem->membername, (paused ? "PAUSE" : "UNPAUSE"), "%s", "");
03325 
03326             manager_event(EVENT_FLAG_AGENT, "QueueMemberPaused",
03327                "Queue: %s\r\n"
03328                "Location: %s\r\n"
03329                "MemberName: %s\r\n"
03330                "Paused: %d\r\n",
03331                   q->name, mem->interface, mem->membername, paused);
03332             ao2_ref(mem, -1);
03333          }
03334       }
03335       ast_mutex_unlock(&q->lock);
03336    }
03337    AST_LIST_UNLOCK(&queues);
03338 
03339    return found ? RESULT_SUCCESS : RESULT_FAILURE;
03340 }

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

sets the QUEUESTATUS channel variable

Definition at line 471 of file app_queue.c.

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

Referenced by queue_exec().

00472 {
00473    int i;
00474 
00475    for (i = 0; i < sizeof(queue_results) / sizeof(queue_results[0]); i++) {
00476       if (queue_results[i].id == res) {
00477          pbx_builtin_setvar_helper(chan, "QUEUESTATUS", queue_results[i].text);
00478          return;
00479       }
00480    }
00481 }

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

Producer of the statechange queue.

Definition at line 732 of file app_queue.c.

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

Referenced by load_module(), and unload_module().

00733 {
00734    struct statechange *sc;
00735 
00736    if (!(sc = ast_calloc(1, sizeof(*sc) + strlen(dev) + 1)))
00737       return 0;
00738 
00739    sc->state = state;
00740    strcpy(sc->dev, dev);
00741 
00742    ast_mutex_lock(&device_state.lock);
00743    AST_LIST_INSERT_TAIL(&device_state.state_change_q, sc, entry);
00744    ast_cond_signal(&device_state.cond);
00745    ast_mutex_unlock(&device_state.lock);
00746 
00747    return 0;
00748 }

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

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

01992 {
01993    struct callattempt *best = find_best(outgoing);
01994 
01995    if (best) {
01996       /* Ring just the best channel */
01997       if (option_debug)
01998          ast_log(LOG_DEBUG, "Next is '%s' with metric %d\n", best->interface, best->metric);
01999       qe->parent->rrpos = best->metric % 1000;
02000    } else {
02001       /* Just increment rrpos */
02002       if (qe->parent->wrapped) {
02003          /* No more channels, start over */
02004          qe->parent->rrpos = 0;
02005       } else {
02006          /* Prioritize next entry */
02007          qe->parent->rrpos++;
02008       }
02009    }
02010    qe->parent->wrapped = 0;
02011 
02012    return 0;
02013 }

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

Definition at line 495 of file app_queue.c.

References name, and strategies.

Referenced by queue_set_param().

00496 {
00497    int x;
00498 
00499    for (x = 0; x < sizeof(strategies) / sizeof(strategies[0]); x++) {
00500       if (!strcasecmp(strategy, strategies[x].name))
00501          return strategies[x].strategy;
00502    }
00503 
00504    return -1;
00505 }

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

References ast_channel::_softhangup, ast_channel::_state, queue_ent::announce, ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), app, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_call(), ast_calloc, AST_CDR_FLAG_LOCKED, ast_cdr_setdestchan(), ast_channel_datastore_add(), ast_channel_datastore_alloc(), ast_channel_datastore_find(), ast_channel_datastore_free(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_make_compatible(), ast_channel_sendurl(), ast_channel_setoption(), ast_channel_supports_html(), ast_channel_unlock, ast_clear_flag, AST_DEVICE_NOT_INUSE, AST_DIGIT_ANY, AST_FEATURE_AUTOMON, AST_FEATURE_DISCONNECT, AST_FEATURE_REDIRECT, ast_hangup(), AST_LIST_HEAD, AST_LIST_HEAD_INIT, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_log(), AST_MAX_CONTEXT, AST_MAX_EXTENSION, ast_moh_stop(), ast_monitor_setjoinfiles(), ast_monitor_start(), ast_mutex_lock(), ast_mutex_unlock(), AST_OPTION_TONE_VERIFY, AST_PBX_NO_HANGUP_PEER, ast_queue_log(), ast_random(), ast_safe_sleep(), ast_say_number(), ast_set_flag, AST_STATE_UP, ast_strdupa, ast_strlen_zero(), ast_test_flag, calc_metric(), ast_channel::cdr, callattempt::chan, queue_ent::chan, ast_channel::context, ast_datastore::data, DATASTORE_INHERIT_FOREVER, di, dialed_interface_info, EVENT_FLAG_AGENT, call_queue::eventwhencalled, queue_ent::expire, ast_channel::exten, free, queue_ent::handled, hangupcalls(), ast_datastore::inheritance, member::interface, member::lastcall, leave_queue(), call_queue::lock, LOG_DEBUG, LOG_NOTICE, LOG_WARNING, manager_event(), callattempt::member, call_queue::membercount, call_queue::memberdelay, member::membername, call_queue::members, call_queue::monfmt, call_queue::monjoin, call_queue::montype, call_queue::name, queue_ent::opos, option_debug, queue_ent::parent, pbx_builtin_getvar_helper(), pbx_builtin_setvar_helper(), pbx_exec(), pbx_findapp(), pbx_substitute_variables_helper(), queue_ent::pending, play_file(), queue_ent::pos, QUEUE_EVENT_VARIABLES, QUEUE_STRATEGY_ROUNDROBIN, recalc_holdtime(), record_abandoned(), call_queue::reportholdtime, ring_one(), member::ringcount, call_queue::ringlimit, call_queue::servicelevel, call_queue::setinterfacevar, call_queue::sound_lessthan, call_queue::sound_minutes, call_queue::sound_reporthold, queue_ent::start, member::status, store_next(), call_queue::strategy, ast_channel::tech, call_queue::timeout, queue_ent::tries, ast_channel_tech::type, ast_cdr::uniqueid, update_queue(), vars2manager(), and wait_for_answer().

Referenced by queue_exec().

02593 {
02594    struct member *cur;
02595    struct callattempt *outgoing = NULL; /* the list of calls we are building */
02596    int to;
02597    char oldexten[AST_MAX_EXTENSION]="";
02598    char oldcontext[AST_MAX_CONTEXT]="";
02599    char queuename[256]="";
02600    struct ast_channel *peer;
02601    struct ast_channel *which;
02602    struct callattempt *lpeer;
02603    struct member *member;
02604    struct ast_app *app;
02605    int res = 0, bridge = 0;
02606    int numbusies = 0;
02607    int x=0;
02608    char *announce = NULL;
02609    char digit = 0;
02610    time_t callstart;
02611    time_t now = time(NULL);
02612    struct ast_bridge_config bridge_config;
02613    char nondataquality = 1;
02614    char *agiexec = NULL;
02615    int ret = 0;
02616    const char *monitorfilename;
02617    const char *monitor_exec;
02618    const char *monitor_options;
02619    char tmpid[256], tmpid2[256];
02620    char meid[1024], meid2[1024];
02621    char mixmonargs[1512];
02622    struct ast_app *mixmonapp = NULL;
02623    char *p;
02624    char vars[2048];
02625    int forwardsallowed = 1;
02626    int callcompletedinsl;
02627    struct ao2_iterator memi;
02628    struct ast_datastore *datastore;
02629 
02630    ast_channel_lock(qe->chan);
02631    datastore = ast_channel_datastore_find(qe->chan, &dialed_interface_info, NULL);
02632    ast_channel_unlock(qe->chan);
02633 
02634    memset(&bridge_config, 0, sizeof(bridge_config));
02635    time(&now);
02636       
02637    for (; options && *options; options++)
02638       switch (*options) {
02639       case 't':
02640          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_REDIRECT);
02641          break;
02642       case 'T':
02643          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_REDIRECT);
02644          break;
02645       case 'w':
02646          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_AUTOMON);
02647          break;
02648       case 'W':
02649          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
02650          break;
02651       case 'd':
02652          nondataquality = 0;
02653          break;
02654       case 'h':
02655          ast_set_flag(&(bridge_config.features_callee), AST_FEATURE_DISCONNECT);
02656          break;
02657       case 'H':
02658          ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT);
02659          break;
02660       case 'n':
02661          if (qe->parent->strategy == QUEUE_STRATEGY_ROUNDROBIN || qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY)
02662             (*tries)++;
02663          else
02664             *tries = qe->parent->membercount;
02665          *noption = 1;
02666          break;
02667       case 'i':
02668          forwardsallowed = 0;
02669          break;
02670       }
02671 
02672    /* Hold the lock while we setup the outgoing calls */
02673    if (use_weight)
02674       AST_LIST_LOCK(&queues);
02675    ast_mutex_lock(&qe->parent->lock);
02676    if (option_debug)
02677       ast_log(LOG_DEBUG, "%s is trying to call a queue member.\n",
02678                      qe->chan->name);
02679    ast_copy_string(queuename, qe->parent->name, sizeof(queuename));
02680    if (!ast_strlen_zero(qe->announce))
02681       announce = qe->announce;
02682    if (!ast_strlen_zero(announceoverride))
02683       announce = announceoverride;
02684 
02685    memi = ao2_iterator_init(qe->parent->members, 0);
02686    while ((cur = ao2_iterator_next(&memi))) {
02687       struct callattempt *tmp = ast_calloc(1, sizeof(*tmp));
02688       struct ast_dialed_interface *di;
02689       AST_LIST_HEAD(, ast_dialed_interface) *dialed_interfaces;
02690       if (!tmp) {
02691          ao2_ref(cur, -1);
02692          ast_mutex_unlock(&qe->parent->lock);
02693          if (use_weight)
02694             AST_LIST_UNLOCK(&queues);
02695          goto out;
02696       }
02697       if (!datastore) {
02698          if (!(datastore = ast_channel_datastore_alloc(&dialed_interface_info, NULL))) {
02699             ao2_ref(cur, -1);
02700             ast_mutex_unlock(&qe->parent->lock);
02701             if (use_weight)
02702                AST_LIST_UNLOCK(&queues);
02703             free(tmp);
02704             goto out;
02705          }
02706          datastore->inheritance = DATASTORE_INHERIT_FOREVER;
02707          if (!(dialed_interfaces = ast_calloc(1, sizeof(*dialed_interfaces)))) {
02708             ao2_ref(cur, -1);
02709             ast_mutex_unlock(&qe->parent->lock);
02710             if (use_weight)
02711                AST_LIST_UNLOCK(&queues);
02712             free(tmp);
02713             goto out;
02714          }
02715          datastore->data = dialed_interfaces;
02716          AST_LIST_HEAD_INIT(dialed_interfaces);
02717 
02718          ast_channel_lock(qe->chan);
02719          ast_channel_datastore_add(qe->chan, datastore);
02720          ast_channel_unlock(qe->chan);
02721       } else
02722          dialed_interfaces = datastore->data;
02723 
02724       AST_LIST_LOCK(dialed_interfaces);
02725       AST_LIST_TRAVERSE(dialed_interfaces, di, list) {
02726          if (!strcasecmp(cur->interface, di->interface)) {
02727             ast_log(LOG_DEBUG, "Skipping dialing interface '%s' since it has already been dialed\n", 
02728                di->interface);
02729             break;
02730          }
02731       }
02732       AST_LIST_UNLOCK(dialed_interfaces);
02733       
02734       if (di) {
02735          free(tmp);
02736          continue;
02737       }
02738 
02739       /* It is always ok to dial a Local interface.  We only keep track of
02740        * which "real" interfaces have been dialed.  The Local channel will
02741        * inherit this list so that if it ends up dialing a real interface,
02742        * it won't call one that has already been called. */
02743       if (strncasecmp(cur->interface, "Local/", 6)) {
02744          if (!(di = ast_calloc(1, sizeof(*di) + strlen(cur->interface)))) {
02745             ao2_ref(cur, -1);
02746             ast_mutex_unlock(&qe->parent->lock);
02747             if (use_weight)
02748                AST_LIST_UNLOCK(&queues);
02749             free(tmp);
02750             goto out;
02751          }
02752          strcpy(di->interface, cur->interface);
02753 
02754          AST_LIST_LOCK(dialed_interfaces);
02755          AST_LIST_INSERT_TAIL(dialed_interfaces, di, list);
02756          AST_LIST_UNLOCK(dialed_interfaces);
02757       }
02758 
02759       tmp->stillgoing = -1;
02760       tmp->member = cur;
02761       tmp->oldstatus = cur->status;
02762       tmp->lastcall = cur->lastcall;
02763       ast_copy_string(tmp->interface, cur->interface, sizeof(tmp->interface));
02764       if (qe->tries == 0 && (cur->ringcount >= qe->parent->ringlimit)) {
02765          cur->ringcount = 0;
02766       }
02767       /* Special case: If we ring everyone, go ahead and ring them, otherwise
02768          just calculate their metric for the appropriate strategy */
02769       if (!calc_metric(qe->parent, cur, x++, qe, tmp)) {
02770          /* Put them in the list of outgoing thingies...  We're ready now.
02771             XXX If we're forcibly removed, these outgoing calls won't get
02772             hung up XXX */
02773          tmp->q_next = outgoing;
02774          outgoing = tmp;      
02775          /* If this line is up, don't try anybody else */
02776          if (outgoing->chan && (outgoing->chan->_state == AST_STATE_UP))
02777             break;
02778       } else {
02779          ao2_ref(cur, -1);
02780          free(tmp);
02781       }
02782    }
02783    if (qe->expire && (!qe->parent->timeout || (qe->expire - now) <= qe->parent->timeout))
02784       to = (qe->expire - now) * 1000;
02785    else
02786       to = (qe->parent->timeout) ? qe->parent->timeout * 1000 : -1;
02787    ++qe->pending;
02788    ++qe->tries;
02789    if (option_debug)
02790       ast_log(LOG_DEBUG, "%s is trying to ring one member from %s. This is try number %d\n",
02791                   qe->chan->name, queuename, qe->tries);
02792    ast_mutex_unlock(&qe->parent->lock);
02793    ring_one(qe, outgoing, &numbusies);
02794    if (use_weight)
02795       AST_LIST_UNLOCK(&queues);
02796    lpeer = wait_for_answer(qe, outgoing, &to, &digit, numbusies, ast_test_flag(&(bridge_config.features_caller), AST_FEATURE_DISCONNECT), forwardsallowed);
02797    /* The ast_channel_datastore_remove() function could fail here if the
02798     * datastore was moved to another channel during a masquerade. If this is
02799     * the case, don't free the datastore here because later, when the channel
02800     * to which the datastore was moved hangs up, it will attempt to free this
02801     * datastore again, causing a crash
02802     */
02803    if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
02804       ast_channel_datastore_free(datastore);
02805    }
02806    ast_mutex_lock(&qe->parent->lock);
02807    if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
02808       store_next(qe, outgoing);
02809    }
02810    ast_mutex_unlock(&qe->parent->lock);
02811    peer = lpeer ? lpeer->chan : NULL;
02812    if (!peer) {
02813       qe->pending = 0;
02814       if (to) {
02815          /* Must gotten hung up */
02816          res = -1;
02817       } else {
02818          /* User exited by pressing a digit */
02819          res = digit;
02820       }
02821       if (queue_debug && res == -1)
02822          ast_log(LOG_NOTICE, "%s: Nobody answered.\n", qe->chan->name);
02823       if (qe->parent->eventwhencalled) {
02824          manager_event(EVENT_FLAG_AGENT, "AgentTimeout",
02825                               "Queue: %s\r\n"
02826                               "ChannelCalling: %s\r\n"
02827                               "Uniqueid: %s\r\n"
02828                               "Tries: %d\r\n"
02829                               "Holdtime: %ld\r\n",
02830                               queuename, qe->chan->name, qe->chan->uniqueid, qe->tries,
02831                               (long)time(NULL) - qe->start);
02832       }
02833    } else { /* peer is valid */
02834       /* Ah ha!  Someone answered within the desired timeframe.  Of course after this
02835          we will always return with -1 so that it is hung up properly after the
02836          conversation.  */
02837       if (!strcmp(qe->chan->tech->type, "Zap"))
02838          ast_channel_setoption(qe->chan, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02839       if (!strcmp(peer->tech->type, "Zap"))
02840          ast_channel_setoption(peer, AST_OPTION_TONE_VERIFY, &nondataquality, sizeof(nondataquality), 0);
02841       /* Update parameters for the queue */
02842       time(&now);
02843       recalc_holdtime(qe, (now - qe->start));
02844       ast_mutex_lock(&qe->parent->lock);
02845       callcompletedinsl = ((now - qe->start) <= qe->parent->servicelevel);
02846       ast_mutex_unlock(&qe->parent->lock);
02847       member = lpeer->member;
02848       /* Increment the refcount for this member, since we're going to be using it for awhile in here. */
02849       ao2_ref(member, 1);
02850       hangupcalls(outgoing, peer);
02851       outgoing = NULL;
02852       if (announce || qe->parent->reportholdtime || qe->parent->memberdelay) {
02853          int res2;
02854 
02855          res2 = ast_autoservice_start(qe->chan);
02856          if (!res2) {
02857             if (qe->parent->memberdelay) {
02858                ast_log(LOG_NOTICE, "Delaying member connect for %d seconds\n", qe->parent->memberdelay);
02859                res2 |= ast_safe_sleep(peer, qe->parent->memberdelay * 1000);
02860             }
02861             if (!res2 && announce) {
02862                play_file(peer, announce);
02863             }
02864             if (!res2 && qe->parent->reportholdtime) {
02865                if (!play_file(peer, qe->parent->sound_reporthold)) {
02866                   int holdtime;
02867 
02868                   time(&now);
02869                   holdtime = abs((now - qe->start) / 60);
02870                   if (holdtime < 2) {
02871                      play_file(peer, qe->parent->sound_lessthan);
02872                      ast_say_number(peer, 2, AST_DIGIT_ANY, peer->language, NULL);
02873                   } else
02874                      ast_say_number(peer, holdtime, AST_DIGIT_ANY, peer->language, NULL);
02875                   play_file(peer, qe->parent->sound_minutes);
02876                }
02877             }
02878          }
02879          res2 |= ast_autoservice_stop(qe->chan);
02880          if (peer->_softhangup) {
02881             /* Agent must have hung up */
02882             ast_log(LOG_WARNING, "Agent on %s hungup on the customer.\n", peer->name);
02883             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "AGENTDUMP", "%s", "");
02884             if (qe->parent->eventwhencalled)
02885                manager_event(EVENT_FLAG_AGENT, "AgentDump",
02886                      "Queue: %s\r\n"
02887                      "Uniqueid: %s\r\n"
02888                      "Channel: %s\r\n"
02889                      "Member: %s\r\n"
02890                      "MemberName: %s\r\n"
02891                      "%s",
02892                      queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
02893                      qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
02894             ast_hangup(peer);
02895             ao2_ref(member, -1);
02896             goto out;
02897          } else if (res2) {
02898             /* Caller must have hung up just before being connected*/
02899             ast_log(LOG_NOTICE, "Caller was about to talk to agent on %s but the caller hungup.\n", peer->name);
02900             ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "ABANDON", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02901             record_abandoned(qe);
02902             ast_hangup(peer);
02903             ao2_ref(member, -1);
02904             return -1;
02905          }
02906       }
02907       /* Stop music on hold */
02908       ast_moh_stop(qe->chan);
02909       /* If appropriate, log that we have a destination channel */
02910       if (qe->chan->cdr)
02911          ast_cdr_setdestchan(qe->chan->cdr, peer->name);
02912       /* Make sure channels are compatible */
02913       res = ast_channel_make_compatible(qe->chan, peer);
02914       if (res < 0) {
02915          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "SYSCOMPAT", "%s", "");
02916          ast_log(LOG_WARNING, "Had to drop call because I couldn't make %s compatible with %s\n", qe->chan->name, peer->name);
02917          record_abandoned(qe);
02918          ast_hangup(peer);
02919          ao2_ref(member, -1);
02920          return -1;
02921       }
02922 
02923       if (qe->parent->setinterfacevar)
02924             pbx_builtin_setvar_helper(qe->chan, "MEMBERINTERFACE", member->interface);
02925 
02926       /* Begin Monitoring */
02927       if (qe->parent->monfmt && *qe->parent->monfmt) {
02928          if (!qe->parent->montype) {
02929             if (option_debug)
02930                ast_log(LOG_DEBUG, "Starting Monitor as requested.\n");
02931             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02932             if (pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC") || pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC_ARGS"))
02933                which = qe->chan;
02934             else
02935                which = peer;
02936             if (monitorfilename)
02937                ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1 );
02938             else if (qe->chan->cdr)
02939                ast_monitor_start(which, qe->parent->monfmt, qe->chan->cdr->uniqueid, 1 );
02940             else {
02941                /* Last ditch effort -- no CDR, make up something */
02942                snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02943                ast_monitor_start(which, qe->parent->monfmt, tmpid, 1 );
02944             }
02945             if (qe->parent->monjoin)
02946                ast_monitor_setjoinfiles(which, 1);
02947          } else {
02948             if (option_debug)
02949                ast_log(LOG_DEBUG, "Starting MixMonitor as requested.\n");
02950             monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME");
02951             if (!monitorfilename) {
02952                if (qe->chan->cdr)
02953                   ast_copy_string(tmpid, qe->chan->cdr->uniqueid, sizeof(tmpid)-1);
02954                else
02955                   snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random());
02956             } else {
02957                ast_copy_string(tmpid2, monitorfilename, sizeof(tmpid2)-1);
02958                for (p = tmpid2; *p ; p++) {
02959                   if (*p == '^' && *(p+1) == '{') {
02960                      *p = '$';
02961                   }
02962                }
02963 
02964                memset(tmpid, 0, sizeof(tmpid));
02965                pbx_substitute_variables_helper(qe->chan, tmpid2, tmpid, sizeof(tmpid) - 1);
02966             }
02967 
02968             monitor_exec = pbx_builtin_getvar_helper(qe->chan, "MONITOR_EXEC");
02969             monitor_options = pbx_builtin_getvar_helper(qe->chan, "MONITOR_OPTIONS");
02970 
02971             if (monitor_exec) {
02972                ast_copy_string(meid2, monitor_exec, sizeof(meid2)-1);
02973                for (p = meid2; *p ; p++) {
02974                   if (*p == '^' && *(p+1) == '{') {
02975                      *p = '$';
02976                   }
02977                }
02978 
02979                memset(meid, 0, sizeof(meid));
02980                pbx_substitute_variables_helper(qe->chan, meid2, meid, sizeof(meid) - 1);
02981             }
02982    
02983             snprintf(tmpid2, sizeof(tmpid2)-1, "%s.%s", tmpid, qe->parent->monfmt);
02984 
02985             mixmonapp = pbx_findapp("MixMonitor");
02986 
02987             if (strchr(tmpid2, '|')) {
02988                ast_log(LOG_WARNING, "monitor-format (in queues.conf) and MONITOR_FILENAME cannot contain a '|'! Not recording.\n");
02989                mixmonapp = NULL;
02990             }
02991 
02992             if (!monitor_options)
02993                monitor_options = "";
02994             
02995             if (strchr(monitor_options, '|')) {
02996                ast_log(LOG_WARNING, "MONITOR_OPTIONS cannot contain a '|'! Not recording.\n");
02997                mixmonapp = NULL;
02998             }
02999 
03000             if (mixmonapp) {
03001                if (!ast_strlen_zero(monitor_exec))
03002                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s|%s", tmpid2, monitor_options, monitor_exec);
03003                else
03004                   snprintf(mixmonargs, sizeof(mixmonargs)-1, "%s|b%s", tmpid2, monitor_options);
03005                   
03006                if (option_debug)
03007                   ast_log(LOG_DEBUG, "Arguments being passed to MixMonitor: %s\n", mixmonargs);
03008                /* We purposely lock the CDR so that pbx_exec does not update the application data */
03009                if (qe->chan->cdr)
03010                   ast_set_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03011                ret = pbx_exec(qe->chan, mixmonapp, mixmonargs);
03012                if (qe->chan->cdr)
03013                   ast_clear_flag(qe->chan->cdr, AST_CDR_FLAG_LOCKED);
03014 
03015             } else
03016                ast_log(LOG_WARNING, "Asked to run MixMonitor on this call, but cannot find the MixMonitor app!\n");
03017 
03018          }
03019       }
03020       /* Drop out of the queue at this point, to prepare for next caller */
03021       leave_queue(qe);        
03022       if (!ast_strlen_zero(url) && ast_channel_supports_html(peer)) {
03023          if (option_debug)
03024             ast_log(LOG_DEBUG, "app_queue: sendurl=%s.\n", url);
03025          ast_channel_sendurl(peer, url);
03026       }
03027       if (!ast_strlen_zero(agi)) {
03028          if (option_debug)
03029             ast_log(LOG_DEBUG, "app_queue: agi=%s.\n", agi);
03030          app = pbx_findapp("agi");
03031          if (app) {
03032             agiexec = ast_strdupa(agi);
03033             ret = pbx_exec(qe->chan, app, agiexec);
03034          } else
03035             ast_log(LOG_WARNING, "Asked to execute an AGI on this channel, but could not find application (agi)!\n");
03036       }
03037       qe->handled++;
03038       ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "CONNECT", "%ld|%s", (long)time(NULL) - qe->start, peer->uniqueid);
03039       if (qe->parent->eventwhencalled)
03040          manager_event(EVENT_FLAG_AGENT, "AgentConnect",
03041                "Queue: %s\r\n"
03042                "Uniqueid: %s\r\n"
03043                "Channel: %s\r\n"
03044                "Member: %s\r\n"
03045                "MemberName: %s\r\n"
03046                "Holdtime: %ld\r\n"
03047                "BridgedChannel: %s\r\n"
03048                "%s",
03049                queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03050                (long)time(NULL) - qe->start, peer->uniqueid,
03051                qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03052       ast_copy_string(oldcontext, qe->chan->context, sizeof(oldcontext));
03053       ast_copy_string(oldexten, qe->chan->exten, sizeof(oldexten));
03054       time(&callstart);
03055 
03056       if (member->status == AST_DEVICE_NOT_INUSE)
03057          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);
03058          
03059 
03060       bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
03061 
03062       if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
03063          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
03064             qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
03065             (long) (time(NULL) - callstart));
03066       } else if (qe->chan->_softhangup) {
03067          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETECALLER", "%ld|%ld|%d",
03068             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03069          if (qe->parent->eventwhencalled)
03070             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03071                   "Queue: %s\r\n"
03072                   "Uniqueid: %s\r\n"
03073                   "Channel: %s\r\n"
03074                   "Member: %s\r\n"
03075                   "MemberName: %s\r\n"
03076                   "HoldTime: %ld\r\n"
03077                   "TalkTime: %ld\r\n"
03078                   "Reason: caller\r\n"
03079                   "%s",
03080                   queuename, qe->chan->uniqueid, peer->name, member->interface, member->membername,
03081                   (long)(callstart - qe->start), (long)(time(NULL) - callstart),
03082                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03083       } else {
03084          ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "COMPLETEAGENT", "%ld|%ld|%d",
03085             (long) (callstart - qe->start), (long) (time(NULL) - callstart), qe->opos);
03086          if (qe->parent->eventwhencalled)
03087             manager_event(EVENT_FLAG_AGENT, "AgentComplete",
03088                   "Queue: %s\r\n"
03089                   "Uniqueid: %s\r\n"
03090                   "Channel: %s\r\n"
03091                   "MemberName: %s\r\n"
03092                   "HoldTime: %ld\r\n"
03093                   "TalkTime: %ld\r\n"
03094                   "Reason: agent\r\n"
03095                   "%s",
03096                   queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
03097                   (long)(time(NULL) - callstart),
03098                   qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
03099       }
03100 
03101       if (bridge != AST_PBX_NO_HANGUP_PEER)
03102          ast_hangup(peer);
03103       update_queue(qe->parent, member, callcompletedinsl);
03104       res = bridge ? bridge : 1;
03105       ao2_ref(member, -1);
03106    }
03107 out:
03108    hangupcalls(outgoing, NULL);
03109 
03110    return res;
03111 }

static int unload_module ( void   )  [static]

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

04996 {
04997    int res;
04998 
04999    if (device_state.thread != AST_PTHREADT_NULL) {
05000       device_state.stop = 1;
05001       ast_mutex_lock(&device_state.lock);
05002       ast_cond_signal(&device_state.cond);
05003       ast_mutex_unlock(&device_state.lock);
05004       pthread_join(device_state.thread, NULL);
05005    }
05006 
05007    ast_cli_unregister_multiple(cli_queue, sizeof(cli_queue) / sizeof(struct ast_cli_entry));
05008    res = ast_manager_unregister("QueueStatus");
05009    res |= ast_manager_unregister("Queues");
05010    res |= ast_manager_unregister("QueueStatus");
05011    res |= ast_manager_unregister("QueueAdd");
05012    res |= ast_manager_unregister("QueueRemove");
05013    res |= ast_manager_unregister("QueuePause");
05014    res |= ast_unregister_application(app_aqm);
05015    res |= ast_unregister_application(app_rqm);
05016    res |= ast_unregister_application(app_pqm);
05017    res |= ast_unregister_application(app_upqm);
05018    res |= ast_unregister_application(app_ql);
05019    res |= ast_unregister_application(app);
05020    res |= ast_custom_function_unregister(&queueagentcount_function);
05021    res |= ast_custom_function_unregister(&queuemembercount_function);
05022    res |= ast_custom_function_unregister(&queuememberlist_function);
05023    res |= ast_custom_function_unregister(&queuewaitingcount_function);
05024    ast_devstate_del(statechange_queue, NULL);
05025 
05026    ast_module_user_hangup_all();
05027 
05028    clear_and_free_interfaces();
05029 
05030    return res;
05031 }

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

Definition at line 2490 of file app_queue.c.

References ast_mutex_lock(), ast_mutex_unlock(), member::calls, call_queue::callscompleted, call_queue::callscompletedinsl, member::lastcall, and call_queue::lock.

Referenced by try_calling().

02491 {
02492    ast_mutex_lock(&q->lock);
02493    time(&member->lastcall);
02494    member->calls++;
02495    q->callscompleted++;
02496    if (callcompletedinsl)
02497       q->callscompletedinsl++;
02498    ast_mutex_unlock(&q->lock);
02499    return 0;
02500 }

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

Definition at line 1303 of file app_queue.c.

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

Referenced by set_member_paused().

01304 {
01305    struct ast_variable *var;
01306    int ret = -1;
01307 
01308    if (!(var = ast_load_realtime("queue_members", "interface", mem->interface, "queue_name", queue_name, NULL))) 
01309       return ret;
01310    while (var) {
01311       if (!strcmp(var->name, "uniqueid"))
01312          break;
01313       var = var->next;
01314    }
01315    if (var && !ast_strlen_zero(var->value)) {
01316       if ((ast_update_realtime("queue_members", "uniqueid", var->value, field, value, NULL)) > -1)
01317          ret = 0;
01318    }
01319    return ret;
01320 }

static void update_realtime_members ( struct call_queue q  )  [static]

Definition at line 1322 of file app_queue.c.

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

Referenced by load_realtime_queue(), and queue_exec().

01323 {
01324    struct ast_config *member_config = NULL;
01325    struct member *m;
01326    char *interface = NULL;
01327    struct ao2_iterator mem_iter;
01328 
01329    if (!(member_config = ast_load_realtime_multientry("queue_members", "interface LIKE", "%", "queue_name", q->name , NULL))) {
01330       /*This queue doesn't have realtime members*/
01331       if (option_debug > 2)
01332          ast_log(LOG_DEBUG, "Queue %s has no realtime members defined. No need for update\n", q->name);
01333       return;
01334    }
01335 
01336    ast_mutex_lock(&q->lock);
01337    
01338    /* Temporarily set realtime  members dead so we can detect deleted ones.*/ 
01339    mem_iter = ao2_iterator_init(q->members, 0);
01340    while ((m = ao2_iterator_next(&mem_iter))) {
01341       if (m->realtime)
01342          m->dead = 1;
01343       ao2_ref(m, -1);
01344    }
01345 
01346    while ((interface = ast_category_browse(member_config, interface))) {
01347       rt_handle_member_record(q, interface,
01348          S_OR(ast_variable_retrieve(member_config, interface, "membername"), interface),
01349          ast_variable_retrieve(member_config, interface, "penalty"),
01350          ast_variable_retrieve(member_config, interface, "paused"));
01351    }
01352 
01353    /* Delete all realtime members that have been deleted in DB. */
01354    mem_iter = ao2_iterator_init(q->members, 0);
01355    while ((m = ao2_iterator_next(&mem_iter))) {
01356       if (m->dead) {
01357          ao2_unlink(q->members, m);
01358          ast_mutex_unlock(&q->lock);
01359          remove_from_interfaces(m->interface);
01360          ast_mutex_lock(&q->lock);
01361          q->membercount--;
01362       }
01363       ao2_ref(m, -1);
01364    }
01365    ast_mutex_unlock(&q->lock);
01366    ast_config_destroy(member_config);
01367 }

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

Definition at line 584 of file app_queue.c.

References ao2_iterator_init(), ao2_iterator_next(), ao2_ref(), AST_LIST_LOCK, AST_LIST_TRAVERSE, AST_LIST_UNLOCK, ast_mutex_lock(), ast_mutex_unlock(), ast_strdupa, member::calls, member::dynamic, EVENT_FLAG_AGENT, member::interface, member::lastcall, call_queue::lock, manager_event(), call_queue::maskmemberstatus, member::membername, call_queue::members, call_queue::name, member::paused, member::penalty, member::realtime, and member::status.

Referenced by handle_statechange(), and ring_entry().

00585 {
00586    struct member *cur;
00587    struct ao2_iterator mem_iter;
00588    struct call_queue *q;
00589 
00590    AST_LIST_LOCK(&queues);
00591    AST_LIST_TRAVERSE(&queues, q, list) {
00592       ast_mutex_lock(&q->lock);
00593       mem_iter = ao2_iterator_init(q->members, 0);
00594       while ((cur = ao2_iterator_next(&mem_iter))) {
00595          char *tmp_interface;
00596          char *slash_pos;
00597          tmp_interface = ast_strdupa(cur->interface);
00598          if ((slash_pos = strchr(tmp_interface, '/')))
00599             if ((slash_pos = strchr(slash_pos + 1, '/')))
00600                *slash_pos = '\0';
00601 
00602          if (strcasecmp(interface, tmp_interface)) {
00603             ao2_ref(cur, -1);
00604             continue;
00605          }
00606 
00607          if (cur->status != status) {
00608             cur->status = status;
00609             if (q->maskmemberstatus) {
00610                ao2_ref(cur, -1);
00611                continue;
00612             }
00613 
00614             manager_event(EVENT_FLAG_AGENT, "QueueMemberStatus",
00615                "Queue: %s\r\n"
00616                "Location: %s\r\n"
00617                "MemberName: %s\r\n"
00618                "Membership: %s\r\n"
00619                "Penalty: %d\r\n"
00620                "CallsTaken: %d\r\n"
00621                "LastCall: %d\r\n"
00622                "Status: %d\r\n"
00623                "Paused: %d\r\n",
00624                q->name, cur->interface, cur->membername, cur->dynamic ? "dynamic" : cur->realtime ? "realtime" : "static",
00625                cur->penalty, cur->calls, (int)cur->lastcall, cur->status, cur->paused);
00626          }
00627          ao2_ref(cur, -1);
00628       }
00629       ast_mutex_unlock(&q->lock);
00630    }
00631    AST_LIST_UNLOCK(&queues);
00632 
00633    return 0;
00634 }

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

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

03497 {
03498    struct ast_module_user *lu;
03499    char *parse;
03500    int priority_jump = 0;
03501    int ignore_fail = 0;
03502    AST_DECLARE_APP_ARGS(args,
03503       AST_APP_ARG(queuename);
03504       AST_APP_ARG(interface);
03505       AST_APP_ARG(options);
03506    );
03507 
03508    if (ast_strlen_zero(data)) {
03509       ast_log(LOG_WARNING, "UnpauseQueueMember requires an argument ([queuename]|interface[|options])\n");
03510       return -1;
03511    }
03512 
03513    parse = ast_strdupa(data);
03514 
03515    AST_STANDARD_APP_ARGS(args, parse);
03516 
03517    lu = ast_module_user_add(chan);
03518 
03519    if (args.options) {
03520       if (strchr(args.options, 'j'))
03521          priority_jump = 1;
03522       if (strchr(args.options, 'i'))
03523          ignore_fail = 1;
03524    }
03525 
03526    if (ast_strlen_zero(args.interface)) {
03527       ast_log(LOG_WARNING, "Missing interface argument to PauseQueueMember ([queuename]|interface[|options])\n");
03528       ast_module_user_remove(lu);
03529       return -1;
03530    }
03531 
03532    if (set_member_paused(args.queuename, args.interface, 0)) {
03533       ast_log(LOG_WARNING, "Attempt to unpause interface %s, not found\n", args.interface);
03534       pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "NOTFOUND");
03535       if (priority_jump || ast_opt_priority_jumping) {
03536          if (!ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) {
03537             ast_module_user_remove(lu);
03538             return 0;
03539          }
03540       }
03541       ast_module_user_remove(lu);
03542       if (ignore_fail) {
03543          return 0;
03544       } else {
03545          return -1;
03546       }
03547    }
03548 
03549    ast_module_user_remove(lu);
03550    pbx_builtin_setvar_helper(chan, "UPQMSTATUS", "UNPAUSED");
03551    return 0;
03552 }

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

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

01500 {
01501    int digitlen = strlen(qe->digits);
01502 
01503    /* Prevent possible buffer overflow */
01504    if (digitlen < sizeof(qe->digits) - 2) {
01505       qe->digits[digitlen] = digit;
01506       qe->digits[digitlen + 1] = '\0';
01507    } else {
01508       qe->digits[0] = '\0';
01509       return 0;
01510    }
01511 
01512    /* If there's no context to goto, short-circuit */
01513    if (ast_strlen_zero(qe->context))
01514       return 0;
01515 
01516    /* If the extension is bad, then reset the digits to blank */
01517    if (!ast_canmatch_extension(qe->chan, qe->context, qe->digits, 1, qe->chan->cid.cid_num)) {
01518       qe->digits[0] = '\0';
01519       return 0;
01520    }
01521 
01522    /* We have an exact match */
01523    if (!ast_goto_if_exists(qe->chan, qe->context, qe->digits, 1)) {
01524       qe->valid_digits = 1;
01525       /* Return 1 on a successful goto */
01526       return 1;
01527    }
01528 
01529    return 0;
01530 }

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

Definition at line 1754 of file app_queue.c.

References pbx_builtin_serialize_variables().

Referenced by ring_entry(), and try_calling().

01755 {
01756    char *tmp = alloca(len);
01757 
01758    if (pbx_builtin_serialize_variables(chan, tmp, len)) {
01759       int i, j;
01760 
01761       /* convert "\n" to "\nVariable: " */
01762       strcpy(vars, "Variable: ");
01763 
01764       for (i = 0, j = 10; (i < len - 1) && (j < len - 1); i++, j++) {
01765          vars[j] = tmp[i];
01766 
01767          if (tmp[i + 1] == '\0')
01768             break;
01769          if (tmp[i] == '\n') {
01770             vars[j++] = '\r';
01771             vars[j++] = '\n';
01772 
01773             ast_copy_string(&(vars[j]), "Variable: ", len - j);
01774             j += 9;
01775          }
01776       }
01777       if (j > len - 3)
01778          j = len - 3;
01779       vars[j++] = '\r';
01780       vars[j++] = '\n';
01781       vars[j] = '\0';
01782    } else {
01783       /* there are no channel variables; leave it blank */
01784       *vars = '\0';
01785    }
01786    return vars;
01787 }

static int wait_a_bit ( struct queue_ent qe  )  [static]

Definition at line 3113 of file app_queue.c.

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

Referenced by queue_exec().

03114 {
03115    /* Don't need to hold the lock while we setup the outgoing calls */
03116    int retrywait = qe->parent->retry * 1000;
03117 
03118    int res = ast_waitfordigit(qe->chan, retrywait);
03119    if (res > 0 && !valid_exit(qe, res))
03120       res = 0;
03121 
03122    return res;
03123 }

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

02102 {
02103    char *queue = qe->parent->name;
02104    struct callattempt *o, *start = NULL, *prev = NULL;
02105    int status;
02106    int numbusies = prebusies;
02107    int numnochan = 0;
02108    int stillgoing = 0;
02109    int orig = *to;
02110    struct ast_frame *f;
02111    struct callattempt *peer = NULL;
02112    struct ast_channel *winner;
02113    struct ast_channel *in = qe->chan;
02114    char on[80] = "";
02115    char membername[80] = "";
02116    long starttime = 0;
02117    long endtime = 0; 
02118 
02119    starttime = (long) time(NULL);
02120    
02121    while (*to && !peer) {
02122       int numlines, retry, pos = 1;
02123       struct ast_channel *watchers[AST_MAX_WATCHERS];
02124       watchers[0] = in;
02125       start = NULL;
02126 
02127       for (retry = 0; retry < 2; retry++) {
02128          numlines = 0;
02129          for (o = outgoing; o; o = o->q_next) { /* Keep track of important channels */
02130             if (o->stillgoing) { /* Keep track of important channels */
02131                stillgoing = 1;
02132                if (o->chan) {
02133                   watchers[pos++] = o->chan;
02134                   if (!start)
02135                      start = o;
02136                   else
02137                      prev->call_next = o;
02138                   prev = o;
02139                }
02140             }
02141             numlines++;
02142          }
02143          if (pos > 1 /* found */ || !stillgoing /* nobody listening */ ||
02144             (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) /* ring would not be delivered */)
02145             break;
02146          /* On "ringall" strategy we only move to the next penalty level
02147             when *all* ringing phones are done in the current penalty level */
02148          ring_one(qe, outgoing, &numbusies);
02149          /* and retry... */
02150       }
02151       if (pos == 1 /* not found */) {
02152          if (numlines == (numbusies + numnochan)) {
02153             ast_log(LOG_DEBUG, "Everyone is busy at this time\n");
02154          } else {
02155             ast_log(LOG_NOTICE, "No one is answering queue '%s' (%d/%d/%d)\n", queue, numlines, numbusies, numnochan);
02156          }
02157          *to = 0;
02158          return NULL;
02159       }
02160       winner = ast_waitfor_n(watchers, pos, to);
02161       for (o = start; o; o = o->call_next) {
02162          if (o->stillgoing && (o->chan) &&  (o->chan->_state == AST_STATE_UP)) {
02163             if (!peer) {
02164                if (option_verbose > 2)
02165                   ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02166                peer = o;
02167             }
02168          } else if (o->chan && (o->chan == winner)) {
02169 
02170             ast_copy_string(on, o->member->interface, sizeof(on));
02171             ast_copy_string(membername, o->member->membername, sizeof(membername));
02172 
02173             if (!ast_strlen_zero(o->chan->call_forward) && !forwardsallowed) {
02174                if (option_verbose > 2)
02175                   ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s' prevented.\n", in->name, o->chan->call_forward);
02176                numnochan++;
02177                do_hang(o);
02178                winner = NULL;
02179                continue;
02180             } else if (!ast_strlen_zero(o->chan->call_forward)) {
02181                char tmpchan[256];
02182                char *stuff;
02183                char *tech;
02184 
02185                ast_copy_string(tmpchan, o->chan->call_forward, sizeof(tmpchan));
02186                if ((stuff = strchr(tmpchan, '/'))) {
02187                   *stuff++ = '\0';
02188                   tech = tmpchan;
02189                } else {
02190                   snprintf(tmpchan, sizeof(tmpchan), "%s@%s", o->chan->call_forward, o->chan->context);
02191                   stuff = tmpchan;
02192                   tech = "Local";
02193                }
02194                /* Before processing channel, go ahead and check for forwarding */
02195                if (option_verbose > 2)
02196                   ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
02197                /* Setup parameters */
02198                o->chan = ast_request(tech, in->nativeformats, stuff, &status);
02199                if (!o->chan) {
02200                   ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
02201                   o->stillgoing = 0;
02202                   numnochan++;
02203                } else {
02204                   ast_channel_inherit_variables(in, o->chan);
02205                   ast_channel_datastore_inherit(in, o->chan);
02206                   if (o->chan->cid.cid_num)
02207                      free(o->chan->cid.cid_num);
02208                   o->chan->cid.cid_num = ast_strdup(in->cid.cid_num);
02209 
02210                   if (o->chan->cid.cid_name)
02211                      free(o->chan->cid.cid_name);
02212                   o->chan->cid.cid_name = ast_strdup(in->cid.cid_name);
02213 
02214                   ast_string_field_set(o->chan, accountcode, in->accountcode);
02215                   o->chan->cdrflags = in->cdrflags;
02216 
02217                   if (in->cid.cid_ani) {
02218                      if (o->chan->cid.cid_ani)
02219                         free(o->chan->cid.cid_ani);
02220                      o->chan->cid.cid_ani = ast_strdup(in->cid.cid_ani);
02221                   }
02222                   if (o->chan->cid.cid_rdnis)
02223                      free(o->chan->cid.cid_rdnis);
02224                   o->chan->cid.cid_rdnis = ast_strdup(S_OR(in->macroexten, in->exten));
02225                   if (ast_call(o->chan, tmpchan, 0)) {
02226                      ast_log(LOG_NOTICE, "Failed to dial on local channel for call forward to '%s'\n", tmpchan);
02227                      do_hang(o);
02228                      numnochan++;
02229                   }
02230                }
02231                /* Hangup the original channel now, in case we needed it */
02232                ast_hangup(winner);
02233                continue;
02234             }
02235             f = ast_read(winner);
02236             if (f) {
02237                if (f->frametype == AST_FRAME_CONTROL) {
02238                   switch (f->subclass) {
02239                   case AST_CONTROL_ANSWER:
02240                      /* This is our guy if someone answered. */
02241                      if (!peer) {
02242                         if (option_verbose > 2)
02243                            ast_verbose( VERBOSE_PREFIX_3 "%s answered %s\n", o->chan->name, in->name);
02244                         peer = o;
02245                      }
02246                      break;
02247                   case AST_CONTROL_BUSY:
02248                      if (option_verbose > 2)
02249                         ast_verbose( VERBOSE_PREFIX_3 "%s is busy\n", o->chan->name);
02250                      if (in->cdr)
02251                         ast_cdr_busy(in->cdr);
02252                      do_hang(o);
02253                      endtime = (long)time(NULL);
02254                      endtime -= starttime;
02255                      rna(endtime*1000, qe, on, membername);
02256                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02257                         if (qe->parent->timeoutrestart)
02258                            *to = orig;
02259                         ring_one(qe, outgoing, &numbusies);
02260                      }
02261                      numbusies++;
02262                      break;
02263                   case AST_CONTROL_CONGESTION:
02264                      if (option_verbose > 2)
02265                         ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
02266                      if (in->cdr)
02267                         ast_cdr_busy(in->cdr);
02268                      endtime = (long)time(NULL);
02269                      endtime -= starttime;
02270                      rna(endtime*1000, qe, on, membername);
02271                      do_hang(o);
02272                      if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02273                         if (qe->parent->timeoutrestart)
02274                            *to = orig;
02275                         ring_one(qe, outgoing, &numbusies);
02276                      }
02277                      numbusies++;
02278                      break;
02279                   case AST_CONTROL_RINGING:
02280                      if (option_verbose > 2)
02281                         ast_verbose( VERBOSE_PREFIX_3 "%s is ringing\n", o->chan->name);
02282                      break;
02283                   case AST_CONTROL_OFFHOOK:
02284                      /* Ignore going off hook */
02285                      break;
02286                   default:
02287                      ast_log(LOG_DEBUG, "Dunno what to do with control type %d\n", f->subclass);
02288                   }
02289                }
02290                ast_frfree(f);
02291             } else {
02292                endtime = (long) time(NULL) - starttime;
02293                rna(endtime * 1000, qe, on, membername);
02294                do_hang(o);
02295                if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
02296                   if (qe->parent->timeoutrestart)
02297                      *to = orig;
02298                   ring_one(qe, outgoing, &numbusies);
02299                }
02300             }
02301          }
02302       }
02303       if (winner == in) {
02304          f = ast_read(in);
02305          if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))) {
02306             /* Got hung up */
02307             *to = -1;
02308             if (f)
02309                ast_frfree(f);
02310             return NULL;
02311          }
02312          /* First check if DTMF digit is a valid exit */
02313          if ((f->frametype == AST_FRAME_DTMF) && valid_exit(qe, f->subclass)) {
02314             if (option_verbose > 3)
02315                ast_verbose(VERBOSE_PREFIX_3 "User pressed digit: %c\n", f->subclass);
02316             *to = 0;
02317             *digit = f->subclass;
02318             ast_frfree(f);
02319             return NULL;
02320          }
02321          /* Else check if DTMF should be interpreted as caller disconnect */
02322          if ((f->frametype == AST_FRAME_DTMF) && caller_disconnect && (f->subclass == '*')) {
02323             if (option_verbose > 3)
02324                ast_verbose(VERBOSE_PREFIX_3 "User hit %c to disconnect call.\n", f->subclass);
02325             *to = 0;
02326             ast_frfree(f);
02327             return NULL;
02328          }
02329          ast_frfree(f);
02330       }
02331       if (!*to) {
02332          for (o = start; o; o = o->call_next)
02333             rna(orig, qe, o->interface, o->member->membername);
02334       }
02335    }
02336 
02337    return peer;
02338 }

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

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

Referenced by queue_exec().

02434 {
02435    int res = 0;
02436 
02437    /* This is the holding pen for callers 2 through maxlen */
02438    for (;;) {
02439       enum queue_member_status stat;
02440 
02441       if (is_our_turn(qe))
02442          break;
02443 
02444       /* If we have timed out, break out */
02445       if (qe->expire && (time(NULL) > qe->expire)) {
02446          *reason = QUEUE_TIMEOUT;
02447          break;
02448       }
02449 
02450       stat = get_member_status(qe->parent, qe->max_penalty);
02451 
02452       /* leave the queue if no agents, if enabled */
02453       if (qe->parent->leavewhenempty && (stat == QUEUE_NO_MEMBERS)) {
02454          *reason = QUEUE_LEAVEEMPTY;
02455          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02456          leave_queue(qe);
02457          break;
02458       }
02459 
02460       /* leave the queue if no reachable agents, if enabled */
02461       if ((qe->parent->leavewhenempty == QUEUE_EMPTY_STRICT) && (stat == QUEUE_NO_REACHABLE_MEMBERS)) {
02462          *reason = QUEUE_LEAVEUNAVAIL;
02463          ast_queue_log(qe->parent->name, qe->chan->uniqueid, "NONE", "EXITEMPTY", "%d|%d|%ld", qe->pos, qe->opos, (long)time(NULL) - qe->start);
02464          leave_queue(qe);
02465          break;
02466       }
02467 
02468       /* Make a position announcement, if enabled */
02469       if (qe->parent->announcefrequency && !ringing &&
02470          (res = say_position(qe)))
02471          break;
02472 
02473       /* Make a periodic announcement, if enabled */
02474       if (qe->parent->periodicannouncefrequency && !ringing &&
02475          (res = say_periodic_announcement(qe)))
02476          break;
02477 
02478       /* Wait a second before checking again */
02479       if ((res = ast_waitfordigit(qe->chan, RECHECK * 1000))) {
02480          if (res > 0 && !valid_exit(qe, res))
02481             res = 0;
02482          else
02483             break;
02484       }
02485    }
02486 
02487    return res;
02488 }


Variable Documentation

char* app = "Queue" [static]

Definition at line 148 of file app_queue.c.

char* app_aqm = "AddQueueMember" [static]

Definition at line 182 of file app_queue.c.

char* app_aqm_descrip [static]

Definition at line 184 of file app_queue.c.

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

Definition at line 183 of file app_queue.c.

char* app_pqm = "PauseQueueMember" [static]

Definition at line 214 of file app_queue.c.

char* app_pqm_descrip [static]

Definition at line 216 of file app_queue.c.

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

Definition at line 215 of file app_queue.c.

char* app_ql = "QueueLog" [static]

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

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

Definition at line 254 of file app_queue.c.

char* app_rqm = "RemoveQueueMember" [static]

Definition at line 198 of file app_queue.c.

char* app_rqm_descrip [static]

Definition at line 200 of file app_queue.c.

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

Definition at line 199 of file app_queue.c.

char* app_upqm = "UnpauseQueueMember" [static]

Definition at line 237 of file app_queue.c.

char* app_upqm_descrip [static]

Definition at line 239 of file app_queue.c.

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

Definition at line 238 of file app_queue.c.

int autofill_default = 0 [static]

queues.conf [general] option

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

struct ast_cli_entry cli_queue[] [static]

Definition at line 4976 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 4971 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 4961 of file app_queue.c.

ast_cond_t cond

Condition for the state change queue

Definition at line 690 of file app_queue.c.

char* descrip [static]

Definition at line 152 of file app_queue.c.

struct { ... } device_state [static]

Data used by the device state thread.

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

enum queue_result id

Definition at line 291 of file app_queue.c.

Referenced by _sip_show_peers().

ast_mutex_t lock

Lock for the state change queue

Definition at line 688 of file app_queue.c.

int montype_default = 0 [static]

queues.conf [general] option

Definition at line 278 of file app_queue.c.

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

Persistent Members astdb family.

Definition at line 261 of file app_queue.c.

char qam_cmd_usage[] [static]

Initial value:

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

Definition at line 4955 of file app_queue.c.

char qrm_cmd_usage[] [static]

Initial value:

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

Definition at line 4958 of file app_queue.c.

int queue_debug = 0 [static]

queues.conf [general] extra debug option

Definition at line 266 of file app_queue.c.

int queue_persistent_members = 0 [static]

queues.conf [general] option

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

struct ast_custom_function queueagentcount_function [static]

Definition at line 4184 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuemembercount_function [static]

Definition at line 4194 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuememberlist_function [static]

Definition at line 4218 of file app_queue.c.

Referenced by load_module(), and unload_module().

struct ast_custom_function queuewaitingcount_function [static]

Definition at line 4209 of file app_queue.c.

Referenced by load_module(), and unload_module().

unsigned int stop

Set to 1 to stop the thread

Definition at line 684 of file app_queue.c.

Referenced by handle_controlstreamfile(), and queue_exec().

struct strategy strategies[] [static]

Referenced by int2strat(), and strat2int().

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

Definition at line 150 of file app_queue.c.

char* text

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

int use_weight = 0 [static]

queues.conf per-queue weight option

Definition at line 272 of file app_queue.c.


Generated on Tue Nov 4 13:20:24 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7